Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Gather User Input via Keypad (DTMF Tones) in Java


In this guide, we'll show you how to gather user input during a phone call through the phone's keypad (using DTMF(link takes you to an external page) tones) in your Java Servlets application. By applying this technique, you can create interactive voice response (IVR(link takes you to an external page)) systems and other phone based interfaces for your users. The code snippets in this guide are written using Java and require the Java JDK 7 or higher. They also make use of the Twilio Java SDK(link takes you to an external page).

Let's get started!


Set up your Java Servlets application to receive incoming phone calls

set-up-your-java-servlets-application-to-receive-incoming-phone-calls page anchor

This guide assumes you have already set up your web application to receive incoming phone calls. If you still need to complete this step, check out this guide. It should walk you through the process of buying a Twilio number and configuring your app to receive incoming calls from it.


Collect user input with the <Gather> TwiML verb

collect-user-input-with-the-gather-twiml-verb page anchor

The <Gather> TwiML verb allows us to collect input from the user during a phone call. Gathering user input through the keypad is a core mechanism of Interactive Voice Response (IVR) systems where users can press "1" to connect to one menu of options and press "2" to reach another. These prompts can be accompanied by voice prompts to the caller, using the TwiML <Say> and <Play> verbs. In this example, we will prompt the user to enter a number to connect to a certain department within our little IVR system.

Use <Gather> to collect user input via the keypad (DTMF tones)

use-gather-to-collect-user-input-via-the-keypad-dtmf-tones page anchor

This example returns TwiML containing a <Say> tag nested within <Gather> tag. The user will be prompted to choose sales or support.

Java

_35
import com.twilio.twiml.voice.Gather;
_35
import com.twilio.twiml.voice.Redirect;
_35
import com.twilio.twiml.voice.Say;
_35
import com.twilio.twiml.TwiML;
_35
import com.twilio.twiml.TwiMLException;
_35
import com.twilio.twiml.VoiceResponse;
_35
_35
import javax.servlet.http.HttpServlet;
_35
import javax.servlet.http.HttpServletRequest;
_35
import javax.servlet.http.HttpServletResponse;
_35
import java.io.IOException;
_35
_35
public class VoiceServlet extends HttpServlet {
_35
_35
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
_35
_35
// Create a TwiML response and add our friendly message.
_35
TwiML twiml = new VoiceResponse.Builder()
_35
.gather(
_35
new Gather.Builder()
_35
.numDigits(1)
_35
.say(new Say.Builder("For sales, press 1. For support, press 2.").build())
_35
.build()
_35
)
_35
.redirect(new Redirect.Builder().url("/voice").build())
_35
.build();
_35
_35
response.setContentType("application/xml");
_35
try {
_35
response.getWriter().print(twiml.toXml());
_35
} catch (TwiMLException e) {
_35
throw new RuntimeException(e);
_35
}
_35
}
_35
}

If the user doesn't enter any input after a configurable timeout, Twilio will continue processing the TwiML in the document to determine what should happen next in the call. When the end of the document is reached, Twilio will hang up the call. In the above example, we use the <Redirect> verb to have Twilio request the same URL again, repeating the prompt for the user

If a user were to enter input with the example above, the user would hear the same prompt over and over again regardless of what button you pressed. By default, if the user does enter input in the <Gather>, Twilio will send another HTTP request to the current webhook URL with a POST parameter containing the Digits entered by the user. In the sample above, we weren't handling this input at all. Let's update that logic to also process user input if it is present.

Branch your call logic based on the digits sent by the user

branch-your-call-logic-based-on-the-digits-sent-by-the-user page anchor

By default, <Gather> will 'loop' on the current TwiML URL, requesting the same URL every time input is entered.

Java

_49
import com.twilio.twiml.*;
_49
_49
import javax.servlet.http.HttpServlet;
_49
import javax.servlet.http.HttpServletRequest;
_49
import javax.servlet.http.HttpServletResponse;
_49
import java.io.IOException;
_49
_49
public class VoiceServlet extends HttpServlet {
_49
_49
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
_49
_49
// Create a TwiML response and add our friendly message.
_49
VoiceResponse.Builder builder = new VoiceResponse.Builder();
_49
_49
String digits = request.getParameter("Digits");
_49
if (digits != null) {
_49
switch (digits) {
_49
case "1":
_49
builder.say(new Say.Builder("You selected sales. Good for you!").build());
_49
break;
_49
case "2":
_49
builder.say(new Say.Builder("You need support. We will help!").build());
_49
break;
_49
default:
_49
builder.say(new Say.Builder("Sorry, I don\'t understand that choice.").build());
_49
appendGather(builder);
_49
break;
_49
}
_49
} else {
_49
appendGather(builder);
_49
}
_49
_49
response.setContentType("application/xml");
_49
try {
_49
response.getWriter().print(builder.build().toXml());
_49
} catch (TwiMLException e) {
_49
throw new RuntimeException(e);
_49
}
_49
}
_49
_49
private static void appendGather(VoiceResponse.Builder builder) {
_49
builder.gather(new Gather.Builder()
_49
.numDigits(1)
_49
.say(new Say.Builder("For sales, press 1. For support, press 2.").build())
_49
.build()
_49
)
_49
.redirect(new Redirect.Builder().url("/voice").build());
_49
}
_49
}


Specify an action to take after user input is collected

specify-an-action-to-take-after-user-input-is-collected page anchor

You may want to have an entirely different endpoint in your application handle the processing of user input. This is possible using the "action" attribute of the <Gather> verb. Let's update our example to add a second endpoint that will be responsible for handling user input.

Add another route to handle the input from the user

add-another-route-to-handle-the-input-from-the-user page anchor

This technique creates a separate route to handle user input.

Java

_30
import com.twilio.twiml.*;
_30
_30
import javax.servlet.http.HttpServlet;
_30
import javax.servlet.http.HttpServletRequest;
_30
import javax.servlet.http.HttpServletResponse;
_30
import java.io.IOException;
_30
_30
public class VoiceServlet extends HttpServlet {
_30
_30
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
_30
_30
// Create a TwiML response and add our friendly message.
_30
TwiML twiml = new VoiceResponse.Builder()
_30
.gather(new Gather.Builder()
_30
.numDigits(1)
_30
.action("/gather")
_30
.say(new Say.Builder("For sales, press 1. For support, press 2.").build())
_30
.build()
_30
)
_30
.redirect(new Redirect.Builder().url("/voice").build())
_30
.build();
_30
_30
response.setContentType("application/xml");
_30
try {
_30
response.getWriter().print(twiml.toXml());
_30
} catch (TwiMLException e) {
_30
throw new RuntimeException(e);
_30
}
_30
}
_30
}

Implement an 'action' URL to handle user input

implement-an-action-url-to-handle-user-input page anchor

This technique creates a separate route to handle user input.

Java

_43
import com.twilio.twiml.voice.Redirect;
_43
import com.twilio.twiml.voice.Say;
_43
import com.twilio.twiml.TwiMLException;
_43
import com.twilio.twiml.VoiceResponse;
_43
_43
import javax.servlet.http.HttpServlet;
_43
import javax.servlet.http.HttpServletRequest;
_43
import javax.servlet.http.HttpServletResponse;
_43
import java.io.IOException;
_43
_43
public class GatherServlet extends HttpServlet {
_43
_43
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
_43
_43
// Create a TwiML response and add our friendly message.
_43
VoiceResponse.Builder builder = new VoiceResponse.Builder();
_43
_43
String digits = request.getParameter("Digits");
_43
if (digits != null) {
_43
switch (digits) {
_43
case "1":
_43
builder.say(new Say.Builder("You selected sales. Good for you!").build());
_43
break;
_43
case "2":
_43
builder.say(new Say.Builder("You need support. We will help!").build());
_43
break;
_43
default:
_43
builder.say(new Say.Builder("Sorry, I don\'t understand that choice.").build());
_43
builder.redirect(new Redirect.Builder().url("/voice").build());
_43
break;
_43
}
_43
} else {
_43
builder.redirect(new Redirect.Builder().url("/voice").build());
_43
}
_43
_43
response.setContentType("application/xml");
_43
try {
_43
response.getWriter().print(builder.build().toXml());
_43
} catch (TwiMLException e) {
_43
throw new RuntimeException(e);
_43
}
_43
}
_43
}

The action attribute takes a relative URL which would point to another route your server is capable of handling. Now, instead of conditional logic in a single route, we use actions and redirects to handle our call logic with separate code paths.


If you're building call center type applications in Java Servlets, you might enjoy stepping through full sample applications that implement a full IVR system.


Rate this page: