Skip to content

Commit

Permalink
Merge 3.x to master (#269)
Browse files Browse the repository at this point in the history
* Use 3.0.0-preview1 (#192)

* Use 3.0.0-preview2 (#193)

* Use preview3. (#196)

* Migration guide (#197)

* Using 3.0.0-preview4. (#205)

Use preview4

* Use preview5. (#206)

* Updated README migration section with custom parameters support entries. (#207)

* New Features section (#212)

* Describe handleMessage callback behavior differences between 2.X and 3.X (#216)

* Use preview6. (#218)

* Implement hold functionality (#220)

* Use 3.0.0-beta1 (#223)

* Use Voice SDK 3.0.0-beta2 (#228)

* Task/update proguard rules (#229)

* Add push credential docs (#232)

* Use absolute URL so the Getting Started page resolves images (#236)

* Removed extra space (#238)

* Use Voice SDK 3.0.0-beta3 (#240)

* Use beta4 (#243)

* Update to 3.0.0-beta5 (#245)

* Consume Android Voice SDK 3.0.0-beta6 (#250)

Consume Android Voice SDK 3.0.0-beta6

* Use Voice Android SDK 3.0.0-beta7 (#252)

* Use Voice Android SDK 3.0.0-beta8 (#255)

* Moved out Migration guide and new feature sections  (#257)

* Moved out new features and migration guide sections and created new Docs files

Small updates to titles and renamed 3.x to 3.0 in some cases

* Made the "x" in "2.x" and "3.x" lower case

The "x" in "2.x" and "3.x" was inconsistent - in some cases lower case and in others upper case. This commit changes the "X" to lower case

* Use Voice Android SDK 3.0.0-beta9 (#258)

* Use Voice Android SDK 3.0.0-beta10 (#259)

* Improve param docs (#261)

* Readme update with audio settings guidance (#260)

* Use Voice Android SDK 3.0.0-beta10

* Updated README with troubleshooting audio section. Upadted VoiceActivity - requestAudioFocus now takes AudioManager.OnAudioFocusChangeListener instead of null listener.

* Use Voice Android SDK 3.0.0-beta11 (#262)

* Use Voice SDK 3.0.0_beta12 (#264)

* Use Voice SDK 3.0.0_beta13 (#265)

* Use Voice Android SDK 3.0.0 GA version (#266)
  • Loading branch information
Aaron Alaniz authored Apr 26, 2019
1 parent 51ec69f commit 578b14d
Show file tree
Hide file tree
Showing 11 changed files with 554 additions and 103 deletions.
128 changes: 128 additions & 0 deletions Docs/migration-guide-2.x-3.x.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
## 2.x to 3.x Migration Guide

This section describes API or behavioral changes when upgrading from Voice Android 2.x to Voice Android 3.x. Each section provides code snippets to assist in transitioning to the new API.

1. [Making a Call](#migration1)
2. [Call State](#migration2)
3. [CallInvite Changes](#migration3)
4. [Specifying a Media Region](#migration4)
5. [ConnectOptions & AcceptOptions](#migration5)
6. [Media Establishment & Connectivity](#migration6)
7. [ProGuard Configuration](#migration7)

#### <a name="migration1"></a>Making a Call

In Voice 3.x, the API to make a call has changed from `Voice.call(...)` to `Voice.connect(...)`.

```Java
Call call = Voice.connect(context, accessToken, listener);
```

#### <a name="migration2"></a>Call State

The call state `CallState` has moved to the Call class. It can be referenced as follows:

```Java
Call.State
```

#### <a name="migration3"></a>CallInvite Changes

In Voice Android 3.x, the `MessageListener` no longer raises errors if an invalid message is provided, instead a `boolean` value is returned when `boolean Voice.handleMessage(context, data, listener)` is called. The `boolean` value returns `true` when the provided data resulted in a `CallInvite` or `CancelledCallInvite` raised by the `MessageListener`. If `boolean Voice.handleMessage(context, data, listener)` returns `false` it means the data provided was not a Twilio Voice push message.

The `MessageListener` now raises callbacks for a `CallInvite` or `CancelledCallInvite` as follows:

```Java
boolean valid = handleMessage(context, data, new MessageListener() {
@Override
void onCallInvite(CallInvite callInvite) {
// Show notification to answer or reject call
}

@Override
void onCancelledCallInvite(CancelledCallInvite callInvite) {
// Hide notification
}
});
```

The `CallInvite` has an `accept()` and `reject()` method. The `getState()` method has been removed from the `CallInvite` in favor of distinguishing between call invites and call invite cancellations with discrete stateless objects. While the `CancelledCallInvite` simply provides the `to`, `from`, and `callSid` fields also available in the `CallInvite`. The class method `getCallSid()` can be used to associate a `CallInvite` with a `CancelledCallInvite`.

In Voice Android 2.x passing a `cancel` message into `void Voice.handleMessage(...)` would not raise a callback in the following two cases:

- This callee accepted the call
- This callee rejected the call

However, in Voice Android 3.x passing a `cancel` data message into `handleMessage(...)` will always result in a callback. A callback is raised whenever a valid message is provided to `Voice.handleMessage(...)`.

Note that Twilio will send a `cancel` message to every registered device of the identity that accepts or rejects a call, even the device that accepted or rejected the call.

#### <a name="migration4"></a>Specifying a media region

Previously, a media region could be specified via `Voice.setRegion(String)`. Now this configuration can be provided as part of `ConnectOptions` or `AcceptOptions` as shown below:

```Java
ConnectOptions connectOptions = new ConnectOptions.Builder()
.region(String)
.build();

AcceptOptions acceptOptions = new AcceptOptions.Builder()
.region(String)
.build();
```

#### <a name="migration5"></a>ConnectOptions & AcceptOptions

To support configurability upon making or accepting a call new classes have been added. To create `ConnectOptions` the `ConnectOptions.Builder` must be used. Once `ConnectOptions` is created it can be provided when connecting a `Call` as shown below:

```Java
ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken)
.setParams(params)
.build();
Call call = Voice.connect(context, connectOptions, listener);
```

A `CallInvite` can also be accepted using `AcceptOptions` as shown below:

```Java
AcceptOptions acceptOptions = new AcceptOptions.Builder()
.build();
CallInvite.accept(context, acceptOptions, listener);
```

#### <a name="migration6"></a>Media Establishment & Connectivity

The Voice Android 3.x SDK uses WebRTC. The exchange of real-time media requires the use of Interactive Connectivity Establishment(ICE) to establish a media connection between the client and the media server. In some network environments where network access is restricted it may be necessary to provide ICE servers to establish a media connection. We reccomend using the [Network Traversal Service (NTS)](https://www.twilio.com/stun-turn) to obtain ICE servers. ICE servers can be provided when making or accepting a call by passing them into `ConnectOptions` or `AcceptOptions` in the following way:

```Java
// Obtain the set of ICE servers from your preferred ICE server provider
Set<IceServer> iceServers = new HashSet<>();
iceServers.add(new IceServer("stun:global.stun.twilio.com:3478?transport=udp"));
iceServers.add(new IceServer("turn:global.turn.twilio.com:3478?transport=udp"));
...

IceOptions iceOptions = new IceOptions.Builder()
.iceServers(iceServers)
.build();

ConnectOptions.Builder connectOptionsBuilder = new ConnectOptions.Builder(accessToken)
.iceOptions(iceOptions)
.build();
```

#### <a name="migration7"></a>ProGuard Configuration

To enable ProGuard, follow the [official instructions](https://developer.android.com/studio/build/shrink-code#enabling-gradle) first.

* Open your app module's ProGuard configuration (`proguard-rules.pro` in your app's module in Android Studio)
* Add the following lines at the end of your existing configuration

```
-keep class com.twilio.** { *; }
-keep class org.webrtc.** { *; }
-dontwarn org.webrtc.**
-keep class com.twilio.voice.** { *; }
-keepattributes InnerClasses
```

107 changes: 107 additions & 0 deletions Docs/new-features-3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
## 3.0 New Features

Voice Android 3.0 has a number of new features listed below:

1. [WebRTC](#feature1)
2. [Custom Parameters](#feature2)
3. [Hold](#feature3)
4. [Ringing](#feature4)
5. [Stats](#feature5)
6. [Preferred Audio Codec](#feature6)

#### <a name="feature1"></a>WebRTC

The SDK is built using Chromium WebRTC for Android. This ensures that over time developers will get the best real-time media streaming capabilities available for Android. Additionally, upgrades to new versions of Chromium WebRTC will happen without changing the public API whenever possible.

#### <a name="feature2"></a>Custom Parameters

You can now send parameters from the caller to the callee when you make a call. The key/value data is sent from the Voice SDK to your TwiML Server Application, and passed into TwiML to reach the callee.

##### Sending parameters to your TwiML Server Application for outgoing calls

Parameters can be sent to your TwiML Server Application by specifying them in the `ConnectOptions` builder as follows:

```Java
final Map<String, String> params = new HashMap<>();
params.put("caller_first_name", "alice");
params.put("caller_last_name", "smith");

ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken)
.params(params)
.build();

call = Voice.connect(context, connectOptions, listener);
```

These will arrive as either POST parameters or URL query parameters, depending on which HTTP method you configured for your TwiML Server Application in the [console](https://www.twilio.com/console/voice/twiml/apps).

Once available on your TwiML Server Application you can use them to populate your TwiML response as described in the next section.

##### Getting parameters from your TwiML Server Application for incoming calls

Parameters can be sent to a callee by initiating a TwiML [\<Dial\>](https://www.twilio.com/docs/voice/twiml/dial). Use the `<Parameter>` attribute to specify your key/value parameters as shown below:

```Java
// Pass custom parameters in TwiML
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial answerOnBridge="false" callerId="client:alice">
<Client>
<Identity>bob</Identity>
<Parameter name="caller_first_name" value="alice" />
<Parameter name="caller_last_name" value="smith" />
</Client>
</Dial>
</Response>
```

When the call invite push message arrives to the callee it will have the specified parameters. The key/value parameters can then be retrieved as a Map from the `CallInvite.getCustomParameters()` method.

#### <a name="feature3"></a>Hold

Previously, there was no way to hold a call. Hold can now be called on the `Call` object as follows:

```Java
call.hold(boolean);
```

#### <a name="feature4"></a>Ringing

Ringing is now provided as a call state. A callback corresponding to this state transition is emitted once before the `Call.Listener.onConnected(...)` callback when the callee is being alerted of a Call. The behavior of this callback is determined by the `answerOnBridge` flag provided in the `Dial` verb of your TwiML application associated with this client. If the `answerOnBridge` flag is `false`, which is the default, the `Call.Listener.onConnected(...)` callback will be emitted immediately after `Call.Listener.onRinging(...)`. If the `answerOnBridge` flag is `true`, this will cause the call to emit the onConnected callback only after the call is answered. See [answerOnBridge](https://www.twilio.com/docs/voice/twiml/dial#answeronbridge) for more details on how to use it with the Dial TwiML verb. If the TwiML response contains a Say verb, then the call will emit the `Call.Listener.onConnected(...)` callback immediately after `Call.Listener.onRinging(...)` is raised, irrespective of the value of `answerOnBridge` being set to `true` or `false`.

These changes are added as follows:

```Java
public class Call {

public enum State {
CONNECTING,
RINGING, // State addition
CONNECTED,
DISCONNECTED
}

public interface Listener {
void onConnectFailure(@NonNull Call call, @NonNull CallException callException);
void onRinging(@NonNull Call call); // Callback addition
void onConnected(@NonNull Call call);
void onDisconnected(@NonNull Call call, @Nullable CallException callException);
}
}
```

#### <a name="feature5"></a>Stats

Statistics related to the media in the call can now be retrieved by calling `Call.getStats(StatsListener listener)`. The `StatsListener` returns a `StatsReport` that provides statistics about the local and remote audio in the call.

#### <a name="feature6"></a>Preferred Audio Codec

In Voice Android 3.0, you can provide your preferred audio codecs in the `ConnectOptions` and the `AcceptOptions`. The only audio codec supported by our mobile infrastructure is currently PCMU. Opus is not currently available on our mobile infrastructure. However it will become available in Q1 of 2019. At that point the default audio codec for all 3.0 mobile clients will be Opus. To always use PCMU as the negotiated audio codec instead you can add it as the first codec in the preferAudioCodecs list.

```Java
ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken)
.params(params)
.preferAudioCodecs(Arrays.asList(new OpusCodec(), new PcmuCodec()))
.build();
Call call = Voice.connect(VoiceActivity.this, connectOptions, callListener);
```
91 changes: 89 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
Get started with Voice on Android:

- [Quickstart](#quickstart) - Run the quickstart app
- [New Features](Docs/new-features-3.0.md) - New features in 3.0
- [Migration Guide](Docs/migration-guide-2.x-3.x.md) - Migrating from 2.x to 3.x
- [Emulator Support](#emulator-support) - Android emulator support
- [Reducing APK Size](#reducing-apk-size) - Use ABI splits to reduce APK size
- [Access Tokens](#access-tokens) - Using access tokens
- [Managing Push Credentials](#managing-push-credentials) - Managing Push Credentials
- [Troubleshooting Audio](#troubleshooting-audio) - Troubleshooting Audio
- [More Documentation](#more-documentation) - More documentation related to the Voice Android SDK
- [Twilio Helper Libraries](#twilio-helper-libraries) - TwiML quickstarts.
- [Issues & Support](#issues-and-support) - Filing issues and general support
Expand Down Expand Up @@ -172,7 +175,6 @@ Enter a PSTN number and press the call button to place a call.

<img height="500px" src="https://raw.githubusercontent.com/twilio/voice-quickstart-android/master/images/quickstart/make_call_to_number.png">


## Emulator Support

The SDK supports using emulators except in the following known cases:
Expand Down Expand Up @@ -223,7 +225,7 @@ The access token generated by your server component is a [jwt](https://jwt.io) t
In the Android SDK the access token is used for the following:

1. To make an outgoing call via `Voice.call(Context context, String accessToken, String twiMLParams, Call.Listener listener)`
2. To register or unregister for incoming notifications via GCM or FCM via `Voice.register(Context context, String accessToken, Voice.RegistrationChannel registrationChannel, String registrationToken, RegistrationListener listener)` and `Voice.unregister(Context context, String accessToken, Voice.RegistrationChannel registrationChannel, String registrationToken, RegistrationListener listener)`. Once registered, incoming notifications are handled via a `CallInvite` where you can choose to accept or reject the invite. When accepting the call an access token is not required. Internally the `CallInvite` has its own accessToken that ensures it can connect to our infrastructure.
2. To register or unregister for incoming notifications via GCM or FCM via `Voice.register(String accessToken, Voice.RegistrationChannel registrationChannel, String registrationToken, RegistrationListener listener)` and `Voice.unregister(String accessToken, Voice.RegistrationChannel registrationChannel, String registrationToken, RegistrationListener listener)`. Once registered, incoming notifications are handled via a `CallInvite` where you can choose to accept or reject the invite. When accepting the call an access token is not required. Internally the `CallInvite` has its own accessToken that ensures it can connect to our infrastructure.

### Managing Expiry

Expand Down Expand Up @@ -258,6 +260,91 @@ If you are certain you want to delete a Push Credential you can click on `Delete

Please ensure that after deleting the Push Credential you remove or replace the Push Credential SID when generating new access tokens.

## Troubleshooting Audio

The following sections provide guidance on how to ensure optimal audio quality in your applications.

### Configuring AudioManager
The `AudioManager` configuration guidance below is meant to provide optimal audio experience when in a `Call`. This configuration is inspired by the [WebRTC Android example](https://chromium.googlesource.com/external/webrtc/+/refs/heads/master/examples/androidapp/src/org/appspot/apprtc/AppRTCAudioManager.java) and the [Android documentation](https://developer.android.com/reference/android/media/AudioFocusRequest).

```
private void setAudioFocus(boolean setFocus) {
if (audioManager != null) {
if (setFocus) {
savedAudioMode = audioManager.getMode();
// Request audio focus before making any device switch.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AudioAttributes playbackAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
.setAudioAttributes(playbackAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int i) {
}
})
.build();
audioManager.requestAudioFocus(focusRequest);
} else {
int focusRequestResult = audioManager.requestAudioFocus(new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
}
}, AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
/*
* Start by setting MODE_IN_COMMUNICATION as default audio mode. It is
* required to be in this mode when playout and/or recording starts for
* best possible VoIP performance. Some devices have difficulties with speaker mode
* if this is not set.
*/
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
} else {
audioManager.setMode(savedAudioMode);
audioManager.abandonAudioFocus(null);
}
}
}
```

### Handling Low Headset Volume
If your application experiences low playback volume, we recommend the following snippets:

#### Android N and Below
```
int focusRequestResult = audioManager.requestAudioFocus(new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
}
}, AudioManager.STREAM_VOICE_CALL,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
```

#### Android O and Up :
```
AudioAttributes playbackAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build();
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
.setAudioAttributes(playbackAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int i) {
}
})
.build();
```

## More Documentation

You can find more documentation on getting started as well as our latest Javadoc below:
Expand Down
Loading

0 comments on commit 578b14d

Please sign in to comment.