001/* 002 * Copyright (c) 2013 Nu Echo Inc. All rights reserved. 003 */ 004package com.nuecho.rivr.voicexml.turn.output; 005 006import static com.nuecho.rivr.core.util.Assert.*; 007import static com.nuecho.rivr.voicexml.rendering.voicexml.VoiceXmlDomUtil.*; 008import static com.nuecho.rivr.voicexml.turn.input.VoiceXmlEvent.*; 009 010import java.util.*; 011 012import javax.json.*; 013 014import org.w3c.dom.*; 015 016import com.nuecho.rivr.core.util.*; 017import com.nuecho.rivr.voicexml.dialogue.*; 018import com.nuecho.rivr.voicexml.rendering.voicexml.*; 019import com.nuecho.rivr.voicexml.turn.output.audio.*; 020import com.nuecho.rivr.voicexml.turn.output.grammar.*; 021import com.nuecho.rivr.voicexml.util.json.*; 022 023/** 024 * An {@link Interaction} is a {@link VoiceXmlOutputTurn} that represents a list 025 * of {@link Prompt} with an optional {@link FinalRecognitionWindow} or 026 * {@link FinalRecordingWindow} phase. 027 * <p> 028 * Each {@link Interaction.Prompt} represents a phase of the interaction with a 029 * sequence of {@link AudioItem} and optional speech/DTMF recognition 030 * configurations. 031 * 032 * @author Nu Echo Inc. 033 * @see Interaction.Prompt 034 * @see Interaction.FinalRecognitionWindow 035 * @see Interaction.FinalRecordingWindow 036 */ 037public class Interaction extends VoiceXmlOutputTurn { 038 private static final String INTERACTION_TURN_TYPE = "interaction"; 039 040 private static final String RECORDING_PROPERTY = "recording"; 041 private static final String RECOGNITION_PROPERTY = "recognition"; 042 private static final String PROMPTS_PROPERTY = "prompts"; 043 044 private final List<Prompt> mPrompts; 045 private final FinalRecognitionWindow mFinalRecognitionWindow; 046 private final FinalRecordingWindow mFinalRecordingWindow; 047 048 /** 049 * @param name The name of this turn. Not empty. 050 * @param prompts The list of {@link Prompt}. Not null. 051 */ 052 public Interaction(String name, List<Prompt> prompts) { 053 super(name); 054 Assert.notNull(prompts, "prompts"); 055 mPrompts = new ArrayList<Prompt>(prompts); 056 mFinalRecognitionWindow = null; 057 mFinalRecordingWindow = null; 058 } 059 060 /** 061 * @param name The name of this turn. Not empty. 062 * @param prompts The list of {@link Prompt}. Not null. 063 * @param finalRecognitionWindow The final recognition phase configuration. 064 * Not null. 065 */ 066 public Interaction(String name, List<Prompt> prompts, FinalRecognitionWindow finalRecognitionWindow) { 067 super(name); 068 Assert.notNull(prompts, "prompts"); 069 Assert.notNull(finalRecognitionWindow, "recognition"); 070 mPrompts = new ArrayList<Prompt>(prompts); 071 mFinalRecognitionWindow = finalRecognitionWindow; 072 mFinalRecordingWindow = null; 073 } 074 075 /** 076 * @param name The name of this turn. Not empty. 077 * @param prompts The list of {@link Prompt}. Not null. 078 * @param finalRecordingWindow The final recording phase configuration. Not 079 * null. 080 */ 081 public Interaction(String name, List<Prompt> prompts, FinalRecordingWindow finalRecordingWindow) { 082 super(name); 083 Assert.notNull(prompts, "prompts"); 084 Assert.notNull(finalRecordingWindow, "recording"); 085 mPrompts = new ArrayList<Prompt>(prompts); 086 mFinalRecordingWindow = finalRecordingWindow; 087 mFinalRecognitionWindow = null; 088 } 089 090 public final List<Prompt> getPrompts() { 091 return Collections.unmodifiableList(mPrompts); 092 } 093 094 public final FinalRecognitionWindow getRecognition() { 095 return mFinalRecognitionWindow; 096 } 097 098 public final FinalRecordingWindow getRecording() { 099 return mFinalRecordingWindow; 100 } 101 102 @Override 103 protected final String getOuputTurnType() { 104 return INTERACTION_TURN_TYPE; 105 } 106 107 @Override 108 protected void addTurnProperties(JsonObjectBuilder builder) { 109 JsonUtils.add(builder, PROMPTS_PROPERTY, JsonUtils.toJson(mPrompts)); 110 JsonUtils.add(builder, RECOGNITION_PROPERTY, mFinalRecognitionWindow); 111 JsonUtils.add(builder, RECORDING_PROPERTY, mFinalRecordingWindow); 112 } 113 114 @Override 115 protected void fillVoiceXmlDocument(Document document, Element formElement, VoiceXmlDialogueContext dialogueContext) 116 throws VoiceXmlDocumentRenderingException { 117 DtmfRecognition dtmfGlobalRecognition = factorizeGlobalDtmfRecognition(); 118 SpeechRecognition speechGlobalRecognition = factorizeGlobalSpeechRecognition(); 119 120 processDtmfRecognition(dtmfGlobalRecognition, formElement); 121 processSpeechRecognition(speechGlobalRecognition, formElement); 122 123 boolean hasAtLeastOneField = false; 124 125 Element formItemElement = null; 126 boolean recognitionMergedWithLastPrompt = false; 127 for (int interactionPromptIndex = 0; interactionPromptIndex < mPrompts.size(); interactionPromptIndex++) { 128 Prompt prompt = mPrompts.get(interactionPromptIndex); 129 DtmfRecognition dtmfRecognition = prompt.getDtmfRecognition(); 130 SpeechRecognition speechRecognition = prompt.getSpeechRecognition(); 131 132 boolean usingField; 133 boolean bargeIn = prompt.getDtmfRecognition() != null || prompt.getSpeechRecognition() != null; 134 135 if (dtmfRecognition == null && speechRecognition == null) { 136 formItemElement = DomUtils.appendNewElement(formElement, BLOCK_ELEMENT); 137 usingField = false; 138 } else { 139 formItemElement = DomUtils.appendNewElement(formElement, FIELD_ELEMENT); 140 addBargeIn(prompt, dtmfRecognition, speechRecognition, formItemElement); 141 usingField = true; 142 } 143 144 hasAtLeastOneField |= usingField; 145 146 String formItemName = PROMPT_FORM_ITEM_NAME_PREFIX + interactionPromptIndex; 147 formItemElement.setAttribute(NAME_ATTRIBUTE, formItemName); 148 149 processDtmfRecognition(getLocalDtmfRecognition(dtmfRecognition, dtmfGlobalRecognition), formItemElement); 150 151 processSpeechRecognition(getLocalSpeechRecognition(speechRecognition, speechGlobalRecognition), 152 formItemElement); 153 154 if (usingField) { 155 if (mFinalRecognitionWindow != null 156 && interactionPromptIndex == mPrompts.size() - 1 157 && same(mFinalRecognitionWindow.getDtmfRecognition(), prompt.getDtmfRecognition()) 158 && same(mFinalRecognitionWindow.getSpeechRecognition(), prompt.getSpeechRecognition())) { 159 addDurationProperty(formItemElement, TIMEOUT_PROPERTY, mFinalRecognitionWindow.getNoInputTimeout()); 160 recognitionMergedWithLastPrompt = true; 161 } else { 162 addDurationProperty(formItemElement, TIMEOUT_PROPERTY, Duration.ZERO); 163 } 164 } 165 166 renderPrompts(prompt, formItemElement, dialogueContext, bargeIn); 167 168 if (usingField && !recognitionMergedWithLastPrompt) { 169 Element noInputElement = DomUtils.appendNewElement(formItemElement, NOINPUT_ELEMENT); 170 createAssignation(noInputElement, formItemName, TRUE); 171 DomUtils.appendNewElement(noInputElement, REPROMPT_ELEMENT); 172 } 173 } 174 175 if (mFinalRecognitionWindow != null) { 176 if (!recognitionMergedWithLastPrompt) { 177 hasAtLeastOneField = true; 178 Element recognitionFormItemElement = DomUtils.appendNewElement(formElement, FIELD_ELEMENT); 179 recognitionFormItemElement.setAttribute(NAME_ATTRIBUTE, RECOGNITION_FORM_ITEM_NAME); 180 processDtmfRecognition(getLocalDtmfRecognition(mFinalRecognitionWindow.getDtmfRecognition(), 181 dtmfGlobalRecognition), 182 recognitionFormItemElement); 183 184 processSpeechRecognition(getLocalSpeechRecognition(mFinalRecognitionWindow.getSpeechRecognition(), 185 speechGlobalRecognition), 186 recognitionFormItemElement); 187 188 addDurationProperty(recognitionFormItemElement, 189 TIMEOUT_PROPERTY, 190 mFinalRecognitionWindow.getNoInputTimeout()); 191 } 192 } else if (mFinalRecordingWindow != null) { 193 Element recordingFormItemElement = DomUtils.appendNewElement(formElement, RECORD_ELEMENT); 194 recordingFormItemElement.setAttribute(NAME_ATTRIBUTE, RECORD_FORM_ITEM_NAME); 195 196 Recording recording = mFinalRecordingWindow.getRecording(); 197 DtmfRecognition dtmfTermRecognition = recording.getDtmfTermRecognition(); 198 if (dtmfTermRecognition != null) { 199 processDtmfRecognition(dtmfTermRecognition, recordingFormItemElement); 200 } 201 202 setBooleanAttribute(recordingFormItemElement, BEEP_ATTRIBUTE, recording.getBeep()); 203 setBooleanAttribute(recordingFormItemElement, DTMFTERM_ATTRIBUTE, recording.getDtmfTerm()); 204 205 addDurationProperty(recordingFormItemElement, TIMEOUT_PROPERTY, mFinalRecordingWindow.getNoInputTimeout()); 206 setDurationAttribute(recordingFormItemElement, FINAL_SILENCE_ATTRIBUTE, recording.getFinalSilence()); 207 setDurationAttribute(recordingFormItemElement, MAXTIME_ATTRIBUTE, recording.getMaximumTime()); 208 setAttribute(recordingFormItemElement, TYPE_ATTRIBUTE, recording.getType()); 209 210 Element filledElement = DomUtils.appendNewElement(recordingFormItemElement, FILLED_ELEMENT); 211 212 List<? extends AudioItem> acknowledgeAudioItems = mFinalRecordingWindow.getAcknowledgeAudioItems(); 213 if (!acknowledgeAudioItems.isEmpty()) { 214 createPrompt(null, recordingFormItemElement, dialogueContext, false, acknowledgeAudioItems); 215 } 216 217 String clientSideAssignationDestination = recording.getClientSideAssignationDestination(); 218 if (clientSideAssignationDestination != null) { 219 createAssignation(filledElement, clientSideAssignationDestination, RECORD_FORM_ITEM_NAME); 220 } 221 222 addRecordingResultHandlerScript(recording, filledElement); 223 createGotoSubmit(filledElement); 224 225 Element catchElement = DomUtils.appendNewElement(recordingFormItemElement, CATCH_ELEMENT); 226 catchElement.setAttribute(EVENT_ATTRIBUTE, CONNECTION_DISCONNECT); 227 addRecordingResultHandlerScript(recording, catchElement); 228 addEventHandlerScript(catchElement); 229 createGotoSubmit(catchElement); 230 231 } else { 232 if (hasAtLeastOneField) { 233 Element blockElement = DomUtils.appendNewElement(formElement, BLOCK_ELEMENT); 234 createGotoSubmit(blockElement); 235 } else { 236 createGotoSubmit(formItemElement); 237 } 238 } 239 240 if (hasAtLeastOneField) { 241 createFormLevelFilled(formElement); 242 } 243 } 244 245 private void addRecordingResultHandlerScript(Recording recording, Element parent) { 246 createScript(parent, RIVR_SCOPE_OBJECT 247 + ".addRecordingResult(dialog." 248 + RECORD_FORM_ITEM_NAME 249 + ", dialog." 250 + RECORD_FORM_ITEM_NAME 251 + "$, " 252 + recording.isPostAudioToServer() 253 + ");"); 254 255 } 256 257 private static void renderPrompts(Prompt prompt, 258 Element parentElement, 259 VoiceXmlDialogueContext voiceXmlDialogueContext, 260 boolean bargeIn) throws VoiceXmlDocumentRenderingException { 261 List<? extends AudioItem> audioItems = prompt.getAudioItems(); 262 createPrompt(prompt.getLanguage(), parentElement, voiceXmlDialogueContext, bargeIn, audioItems); 263 } 264 265 private SpeechRecognition factorizeGlobalSpeechRecognition() { 266 List<SpeechRecognition> speechConfigurations = getSpeechRecognitions(); 267 if (speechConfigurations.isEmpty()) return null; 268 269 SpeechRecognition speechGlobalRecognition = speechConfigurations.get(0).copy(); 270 // unfortunately, we cannot use form-level grammars because of VoiceXML 271 // semantic mapping (section 3.1.6) 272 speechGlobalRecognition.setGrammarItems(new GrammarItem[0]); 273 274 for (SpeechRecognition speechConfiguration : speechConfigurations) { 275 SpeechRecognition configuration = speechConfiguration; 276 Assert.notNull(configuration, "configuration"); 277 278 if (!same(configuration.getCompleteTimeout(), speechGlobalRecognition.getCompleteTimeout())) { 279 speechGlobalRecognition.setCompleteTimeout(null); 280 } 281 282 if (!same(configuration.getIncompleteTimeout(), speechGlobalRecognition.getIncompleteTimeout())) { 283 speechGlobalRecognition.setIncompleteTimeout(null); 284 } 285 286 if (!same(configuration.getConfidenceLevel(), speechGlobalRecognition.getConfidenceLevel())) { 287 speechGlobalRecognition.setConfidenceLevel(null); 288 } 289 290 if (!same(configuration.getMaxSpeechTimeout(), speechGlobalRecognition.getMaxSpeechTimeout())) { 291 speechGlobalRecognition.setMaxSpeechTimeout(null); 292 } 293 294 if (!same(configuration.getSensitivity(), speechGlobalRecognition.getSensitivity())) { 295 speechGlobalRecognition.setSensitivity(null); 296 } 297 298 if (!same(configuration.getSpeedVersusAccuracy(), speechGlobalRecognition.getSpeedVersusAccuracy())) { 299 speechGlobalRecognition.setSpeedVersusAccuracy(null); 300 } 301 302 if (!same(configuration.getMaxNBest(), speechGlobalRecognition.getMaxNBest())) { 303 speechGlobalRecognition.setMaxNBest(null); 304 } 305 306 factorizeParameters(speechGlobalRecognition, configuration); 307 } 308 309 return speechGlobalRecognition; 310 } 311 312 private DtmfRecognition factorizeGlobalDtmfRecognition() { 313 List<DtmfRecognition> dtmfRecognitions = getDtmfRecognitions(); 314 if (dtmfRecognitions.isEmpty()) return null; 315 316 DtmfRecognition dtmfGlobalRecognition = dtmfRecognitions.get(0).copy(); 317 // unfortunately, we cannot use form-level grammars because of VoiceXML 318 // semantic mapping (section 3.1.6) 319 dtmfGlobalRecognition.setGrammarItems(new GrammarItem[0]); 320 321 for (DtmfRecognition dtmfRecognition : dtmfRecognitions) { 322 DtmfRecognition configuration = dtmfRecognition; 323 Assert.notNull(configuration, "configuration"); 324 325 if (!same(configuration.getInterDigitTimeout(), dtmfGlobalRecognition.getInterDigitTimeout())) { 326 dtmfGlobalRecognition.setInterDigitTimeout(null); 327 } 328 329 if (!same(configuration.getTermTimeout(), dtmfGlobalRecognition.getTermTimeout())) { 330 dtmfGlobalRecognition.setTermTimeout(null); 331 } 332 333 if (!same(configuration.getTermChar(), dtmfGlobalRecognition.getTermChar())) { 334 dtmfGlobalRecognition.setTermChar(null); 335 } 336 337 factorizeParameters(dtmfGlobalRecognition, configuration); 338 } 339 340 return dtmfGlobalRecognition; 341 } 342 343 private List<SpeechRecognition> getSpeechRecognitions() { 344 List<SpeechRecognition> configurations = new ArrayList<SpeechRecognition>(); 345 346 for (Prompt prompt : mPrompts) { 347 SpeechRecognition speechRecognition = prompt.getSpeechRecognition(); 348 if (speechRecognition != null) { 349 configurations.add(speechRecognition); 350 } 351 352 } 353 354 if (mFinalRecognitionWindow != null) { 355 SpeechRecognition speechRecognition = mFinalRecognitionWindow.getSpeechRecognition(); 356 if (speechRecognition != null) { 357 configurations.add(speechRecognition); 358 } 359 } 360 361 return configurations; 362 } 363 364 private List<DtmfRecognition> getDtmfRecognitions() { 365 List<DtmfRecognition> configurations = new ArrayList<DtmfRecognition>(); 366 367 for (Prompt prompt : getPrompts()) { 368 DtmfRecognition dtmfRecognition = prompt.getDtmfRecognition(); 369 if (dtmfRecognition != null) { 370 configurations.add(dtmfRecognition); 371 } 372 373 } 374 375 if (mFinalRecognitionWindow != null) { 376 DtmfRecognition dtmfRecognition = mFinalRecognitionWindow.getDtmfRecognition(); 377 if (dtmfRecognition != null) { 378 configurations.add(dtmfRecognition); 379 } 380 } 381 382 return configurations; 383 } 384 385 private static boolean same(Object object1, Object object2) { 386 if (object1 == null) return object2 == null; 387 if (object2 == null) return false; 388 return object1.equals(object2); 389 } 390 391 private static void factorizeParameters(Recognition globalRecognition, Recognition localRecognition) { 392 for (String propertyName : globalRecognition.getPropertyNames()) { 393 if (!same(localRecognition.getProperty(propertyName), globalRecognition.getProperty(propertyName))) { 394 globalRecognition.removeProperty(propertyName); 395 } 396 } 397 } 398 399 private static void addBargeIn(Prompt prompt, 400 DtmfRecognition dtmfRecognition, 401 SpeechRecognition speechRecognition, 402 Element formItemElement) { 403 BargeInType bargeInType = prompt.getBargeInType(); 404 if (bargeInType != null) { 405 addProperty(formItemElement, BARGE_IN_TYPE_PROPERTY, bargeInType.name()); 406 } 407 408 if (dtmfRecognition != null && speechRecognition != null) { 409 addProperty(formItemElement, INPUT_MODES_PROPERTY, DTMF_VOICE_INPUT_MODE); 410 } else if (dtmfRecognition == null) { 411 addProperty(formItemElement, INPUT_MODES_PROPERTY, VOICE_INPUT_MODE); 412 } else if (speechRecognition == null) { 413 addProperty(formItemElement, INPUT_MODES_PROPERTY, DTMF_INPUT_MODE); 414 } 415 } 416 417 private static DtmfRecognition getLocalDtmfRecognition(DtmfRecognition dtmfRecognition, 418 DtmfRecognition globalDtmfRecognition) { 419 if (dtmfRecognition == null) return null; 420 421 DtmfRecognition localDtmfRecognition = dtmfRecognition.copy(); 422 423 if (globalDtmfRecognition.getInterDigitTimeout() != null) { 424 localDtmfRecognition.setInterDigitTimeout(null); 425 } 426 427 if (globalDtmfRecognition.getTermTimeout() != null) { 428 localDtmfRecognition.setTermTimeout(null); 429 } 430 431 if (globalDtmfRecognition.getTermChar() != null) { 432 localDtmfRecognition.setTermChar(null); 433 } 434 435 removeGlobalParameters(globalDtmfRecognition, localDtmfRecognition); 436 437 return localDtmfRecognition; 438 } 439 440 private static SpeechRecognition getLocalSpeechRecognition(SpeechRecognition speechRecognition, 441 SpeechRecognition globalSpeechRecognition) { 442 if (speechRecognition == null) return null; 443 444 SpeechRecognition localSpeechRecognition = speechRecognition.copy(); 445 446 if (globalSpeechRecognition.getCompleteTimeout() != null) { 447 localSpeechRecognition.setCompleteTimeout(null); 448 } 449 450 if (globalSpeechRecognition.getIncompleteTimeout() != null) { 451 localSpeechRecognition.setIncompleteTimeout(null); 452 } 453 454 if (globalSpeechRecognition.getConfidenceLevel() != null) { 455 localSpeechRecognition.setConfidenceLevel(null); 456 } 457 458 if (globalSpeechRecognition.getIncompleteTimeout() != null) { 459 localSpeechRecognition.setIncompleteTimeout(null); 460 } 461 462 if (globalSpeechRecognition.getMaxNBest() != null) { 463 localSpeechRecognition.setMaxNBest(null); 464 } 465 466 if (globalSpeechRecognition.getMaxSpeechTimeout() != null) { 467 localSpeechRecognition.setMaxSpeechTimeout(null); 468 } 469 470 if (globalSpeechRecognition.getSensitivity() != null) { 471 localSpeechRecognition.setSensitivity(null); 472 } 473 474 if (globalSpeechRecognition.getSpeedVersusAccuracy() != null) { 475 localSpeechRecognition.setSpeedVersusAccuracy(null); 476 } 477 478 removeGlobalParameters(globalSpeechRecognition, localSpeechRecognition); 479 480 return localSpeechRecognition; 481 } 482 483 private static void removeGlobalParameters(Recognition globalRecognition, Recognition localRecognition) { 484 for (String propertyName : globalRecognition.getPropertyNames()) { 485 localRecognition.removeProperty(propertyName); 486 } 487 } 488 489 private void createFormLevelFilled(Element parent) throws VoiceXmlDocumentRenderingException { 490 Element filledElement = DomUtils.appendNewElement(parent, FILLED_ELEMENT); 491 filledElement.setAttribute(MODE_ATTRIBUTE, ANY_MODE); 492 493 if (mFinalRecognitionWindow != null) { 494 List<? extends AudioItem> acknowledgeAudioItems = mFinalRecognitionWindow.getAcknowledgeAudioItems(); 495 if (!acknowledgeAudioItems.isEmpty()) { 496 processAudioItems(acknowledgeAudioItems, filledElement); 497 } 498 } 499 500 createScript(filledElement, RIVR_SCOPE_OBJECT + ".addRecognitionResult()"); 501 createGotoSubmit(filledElement); 502 } 503 504 /** 505 * Barge-in types. 506 * 507 * @author Nu Echo Inc. 508 */ 509 public enum BargeInType { 510 /** 511 * Normal speech-detection barge-in. Triggers only when speech is 512 * detected. 513 */ 514 speech, 515 516 /** 517 * Hotword barge-in. Triggers only when grammar is matched. 518 */ 519 hotword; 520 } 521 522 /** 523 * A {@link FinalRecognitionWindow} is an optional final phase of an 524 * {@link Interaction}. 525 * <p> 526 * It specifies a recognition configuration, and optionally, a no input 527 * timeout and a sequence of {@link AudioItem} that is played if a 528 * recognition is successful. 529 * 530 * @author Nu Echo Inc. 531 */ 532 public static final class FinalRecognitionWindow implements JsonSerializable { 533 534 private static final String NO_INPUT_TIMEOUT_PROPERTY = "noInputTimeout"; 535 private static final String SPEECH_RECOGNITION_PROPERTY = "speechRecognition"; 536 private static final String DTMF_RECOGNITION_PROPERTY = "dtmfRecognition"; 537 private static final String ACKNOWLEDGE_AUDIO_ITEMS_PROPERTY = "acknowledgeAudioItems"; 538 539 private final SpeechRecognition mSpeechRecognition; 540 private final DtmfRecognition mDtmfRecognition; 541 private Duration mNoInputTimeout; 542 private List<AudioItem> mAcknowledgeAudioItems = new ArrayList<AudioItem>(); 543 544 public FinalRecognitionWindow(DtmfRecognition dtmfRecognition, 545 SpeechRecognition speechRecognition, 546 Duration noInputTimeout) { 547 mSpeechRecognition = speechRecognition; 548 mDtmfRecognition = dtmfRecognition; 549 mNoInputTimeout = noInputTimeout; 550 assertInvariant(); 551 } 552 553 public FinalRecognitionWindow(DtmfRecognition dtmfRecognition, SpeechRecognition speechRecognition) { 554 this(dtmfRecognition, speechRecognition, null); 555 } 556 557 public FinalRecognitionWindow(DtmfRecognition dtmfRecognition) { 558 this(dtmfRecognition, null, null); 559 } 560 561 public FinalRecognitionWindow(SpeechRecognition speechRecognition) { 562 this(null, speechRecognition, null); 563 } 564 565 public FinalRecognitionWindow(DtmfRecognition dtmfRecognition, Duration noInputTimeout) { 566 this(dtmfRecognition, null, noInputTimeout); 567 } 568 569 public FinalRecognitionWindow(SpeechRecognition speechRecognition, Duration noInputTimeout) { 570 this(null, speechRecognition, noInputTimeout); 571 } 572 573 public SpeechRecognition getSpeechRecognition() { 574 return mSpeechRecognition; 575 } 576 577 public DtmfRecognition getDtmfRecognition() { 578 return mDtmfRecognition; 579 } 580 581 public Duration getNoInputTimeout() { 582 return mNoInputTimeout; 583 } 584 585 public List<? extends AudioItem> getAcknowledgeAudioItems() { 586 return Collections.unmodifiableList(mAcknowledgeAudioItems); 587 } 588 589 public void setNoInputTimeout(Duration noInputTimeout) { 590 mNoInputTimeout = noInputTimeout; 591 } 592 593 public void setAcknowledgeAudioItems(List<? extends AudioItem> acknowledgeAudioItems) { 594 Assert.noNullValues(acknowledgeAudioItems, "acknowledgeAudioItems"); 595 mAcknowledgeAudioItems = new ArrayList<AudioItem>(acknowledgeAudioItems); 596 } 597 598 public void setAcknowledgeAudioItems(AudioItem... acknowledgeAudioItems) { 599 setAcknowledgeAudioItems(asListChecked(acknowledgeAudioItems)); 600 } 601 602 private void assertInvariant() { 603 Assert.ensure(mSpeechRecognition != null || mDtmfRecognition != null, 604 "Must provide a non-null configuration for speech or DTMF"); 605 } 606 607 @Override 608 public JsonValue asJson() { 609 JsonObjectBuilder builder = JsonUtils.createObjectBuilder(); 610 JsonUtils.add(builder, ACKNOWLEDGE_AUDIO_ITEMS_PROPERTY, JsonUtils.toJson(mAcknowledgeAudioItems)); 611 JsonUtils.add(builder, DTMF_RECOGNITION_PROPERTY, mDtmfRecognition); 612 JsonUtils.add(builder, SPEECH_RECOGNITION_PROPERTY, mSpeechRecognition); 613 JsonUtils.addDurationProperty(builder, NO_INPUT_TIMEOUT_PROPERTY, mNoInputTimeout); 614 return builder.build(); 615 } 616 617 @Override 618 public int hashCode() { 619 final int prime = 31; 620 int result = 1; 621 result = prime * result + ((mAcknowledgeAudioItems == null) ? 0 : mAcknowledgeAudioItems.hashCode()); 622 result = prime * result + ((mDtmfRecognition == null) ? 0 : mDtmfRecognition.hashCode()); 623 result = prime * result + ((mNoInputTimeout == null) ? 0 : mNoInputTimeout.hashCode()); 624 result = prime * result + ((mSpeechRecognition == null) ? 0 : mSpeechRecognition.hashCode()); 625 return result; 626 } 627 628 @Override 629 public boolean equals(Object obj) { 630 if (this == obj) return true; 631 if (obj == null) return false; 632 if (getClass() != obj.getClass()) return false; 633 FinalRecognitionWindow other = (FinalRecognitionWindow) obj; 634 if (mAcknowledgeAudioItems == null) { 635 if (other.mAcknowledgeAudioItems != null) return false; 636 } else if (!mAcknowledgeAudioItems.equals(other.mAcknowledgeAudioItems)) return false; 637 if (mDtmfRecognition == null) { 638 if (other.mDtmfRecognition != null) return false; 639 } else if (!mDtmfRecognition.equals(other.mDtmfRecognition)) return false; 640 if (mNoInputTimeout == null) { 641 if (other.mNoInputTimeout != null) return false; 642 } else if (!mNoInputTimeout.equals(other.mNoInputTimeout)) return false; 643 if (mSpeechRecognition == null) { 644 if (other.mSpeechRecognition != null) return false; 645 } else if (!mSpeechRecognition.equals(other.mSpeechRecognition)) return false; 646 return true; 647 } 648 649 @Override 650 public String toString() { 651 return asJson().toString(); 652 } 653 } 654 655 /** 656 * A {@link FinalRecordingWindow} is an optional final phase of an 657 * {@link Interaction}. 658 * <p> 659 * It specifies a recording configuration, and optionally, a no input 660 * timeout and a sequence of {@link AudioItem} that is played if a recording 661 * is successful. 662 * 663 * @author Nu Echo Inc. 664 */ 665 public static final class FinalRecordingWindow implements JsonSerializable { 666 private static final String ACKNOWLEDGE_AUDIO_ITEMS_PROPERTY = "acknowledgeAudioItems"; 667 @SuppressWarnings("hiding") 668 private static final String RECORDING_PROPERTY = "recording"; 669 private static final String NO_INPUT_TIMEOUT_PROPERTY = "noInputTimeout"; 670 671 private final Recording mRecording; 672 private Duration mNoInputTimeout; 673 private List<AudioItem> mAcknowledgeAudioItems = new ArrayList<AudioItem>(); 674 675 public FinalRecordingWindow(Recording recording) { 676 this(recording, null); 677 } 678 679 public FinalRecordingWindow(Recording recording, Duration noInputTimeout) { 680 Assert.notNull(recording, "recording"); 681 mRecording = recording; 682 mNoInputTimeout = noInputTimeout; 683 } 684 685 public Recording getRecording() { 686 return mRecording; 687 } 688 689 public Duration getNoInputTimeout() { 690 return mNoInputTimeout; 691 } 692 693 public List<? extends AudioItem> getAcknowledgeAudioItems() { 694 return Collections.unmodifiableList(mAcknowledgeAudioItems); 695 } 696 697 public void setAcknowledgeAudioItems(List<? extends AudioItem> acknowledgeAudioItems) { 698 Assert.noNullValues(acknowledgeAudioItems, "acknowledgeAudioItems"); 699 mAcknowledgeAudioItems = new ArrayList<AudioItem>(acknowledgeAudioItems); 700 } 701 702 public void setAcknowledgeAudioItems(AudioItem... acknowledgeAudioItems) { 703 setAcknowledgeAudioItems(asListChecked(acknowledgeAudioItems)); 704 } 705 706 public void setNoInputTimeout(Duration noInputTimeout) { 707 mNoInputTimeout = noInputTimeout; 708 } 709 710 @Override 711 public JsonValue asJson() { 712 JsonObjectBuilder builder = JsonUtils.createObjectBuilder(); 713 JsonUtils.addDurationProperty(builder, NO_INPUT_TIMEOUT_PROPERTY, mNoInputTimeout); 714 JsonUtils.add(builder, RECORDING_PROPERTY, mRecording); 715 JsonUtils.add(builder, ACKNOWLEDGE_AUDIO_ITEMS_PROPERTY, JsonUtils.toJson(mAcknowledgeAudioItems)); 716 return builder.build(); 717 } 718 719 @Override 720 public int hashCode() { 721 final int prime = 31; 722 int result = 1; 723 result = prime * result + ((mAcknowledgeAudioItems == null) ? 0 : mAcknowledgeAudioItems.hashCode()); 724 result = prime * result + ((mNoInputTimeout == null) ? 0 : mNoInputTimeout.hashCode()); 725 result = prime * result + ((mRecording == null) ? 0 : mRecording.hashCode()); 726 return result; 727 } 728 729 @Override 730 public boolean equals(Object obj) { 731 if (this == obj) return true; 732 if (obj == null) return false; 733 if (getClass() != obj.getClass()) return false; 734 FinalRecordingWindow other = (FinalRecordingWindow) obj; 735 if (mAcknowledgeAudioItems == null) { 736 if (other.mAcknowledgeAudioItems != null) return false; 737 } else if (!mAcknowledgeAudioItems.equals(other.mAcknowledgeAudioItems)) return false; 738 if (mNoInputTimeout == null) { 739 if (other.mNoInputTimeout != null) return false; 740 } else if (!mNoInputTimeout.equals(other.mNoInputTimeout)) return false; 741 if (mRecording == null) { 742 if (other.mRecording != null) return false; 743 } else if (!mRecording.equals(other.mRecording)) return false; 744 return true; 745 } 746 747 @Override 748 public String toString() { 749 return asJson().toString(); 750 } 751 } 752 753 /** 754 * A {@link Prompt} represent a phase in an {@link Interaction} and is 755 * composed of a sequence of {@link AudioItem} and optionally a speech 756 * and/or a DTMF recognition configuration. 757 * 758 * @author Nu Echo Inc. 759 */ 760 public static final class Prompt implements JsonSerializable { 761 private static final String SPEECH_RECOGNITION_PROPERTY = "speechRecognition"; 762 private static final String DTMF_RECOGNITION_PROPERTY = "dtmfRecognition"; 763 @SuppressWarnings("hiding") 764 private static final String BARGE_IN_TYPE_PROPERTY = "bargeInType"; 765 private static final String AUDIO_ITEMS_PROPERTY = "audioItems"; 766 private static final String LANGUAGE_PROPERTY = "language"; 767 768 private final List<AudioItem> mAudioItems; 769 private final SpeechRecognition mSpeechRecognition; 770 private final DtmfRecognition mDtmfRecognition; 771 772 private String mLanguage; 773 private BargeInType mBargeInType; 774 775 /** 776 * @param speechRecognition The speech recognition configuration. 777 * Optional. 778 * @param dtmfRecognition The DTMF recognition configuration. Optional. 779 * @param audioItems The list of {@link AudioItem}. Not null. 780 */ 781 public Prompt(SpeechRecognition speechRecognition, 782 DtmfRecognition dtmfRecognition, 783 List<? extends AudioItem> audioItems) { 784 Assert.noNullValues(audioItems, "audioItems"); 785 mAudioItems = new ArrayList<AudioItem>(audioItems); 786 mSpeechRecognition = speechRecognition; 787 mDtmfRecognition = dtmfRecognition; 788 } 789 790 /** 791 * @param speechRecognition The speech recognition configuration. 792 * @param dtmfRecognition The DTMF recognition configuration. 793 * @param audioItems The list of {@link AudioItem}. Not null. 794 */ 795 public Prompt(SpeechRecognition speechRecognition, DtmfRecognition dtmfRecognition, AudioItem... audioItems) { 796 this(speechRecognition, dtmfRecognition, asListChecked(audioItems)); 797 } 798 799 /** 800 * @param speechRecognition The speech recognition configuration. 801 * @param audioItems The list of {@link AudioItem}. Not null. 802 */ 803 public Prompt(SpeechRecognition speechRecognition, List<? extends AudioItem> audioItems) { 804 this(speechRecognition, null, audioItems); 805 } 806 807 /** 808 * @param speechRecognition The speech recognition configuration. 809 * @param audioItems The list of {@link AudioItem}. Not null. 810 */ 811 public Prompt(SpeechRecognition speechRecognition, AudioItem... audioItems) { 812 this(speechRecognition, null, audioItems); 813 } 814 815 /** 816 * @param dtmfRecognition The DTMF recognition configuration. 817 * @param audioItems The list of {@link AudioItem}. Not null. 818 */ 819 public Prompt(DtmfRecognition dtmfRecognition, List<? extends AudioItem> audioItems) { 820 this(null, dtmfRecognition, audioItems); 821 } 822 823 /** 824 * @param dtmfRecognition The DTMF recognition configuration. 825 * @param audioItems The list of {@link AudioItem}. Not null. 826 */ 827 public Prompt(DtmfRecognition dtmfRecognition, AudioItem... audioItems) { 828 this(null, dtmfRecognition, audioItems); 829 } 830 831 /** 832 * @param audioItems The list of {@link AudioItem}. Not null. 833 */ 834 public Prompt(List<? extends AudioItem> audioItems) { 835 this(null, null, audioItems); 836 } 837 838 /** 839 * @param audioItems The list of {@link AudioItem}. Not null. 840 */ 841 public Prompt(AudioItem... audioItems) { 842 this(null, null, audioItems); 843 } 844 845 /** 846 * @param language language code for this prompt. <code>null</code> if 847 * language should be reset to platform-specific default 848 * value for the prompts to be added. 849 */ 850 public void setLanguage(String language) { 851 mLanguage = language; 852 } 853 854 /** 855 * @param bargeInType {@link BargeInType#speech} or 856 * {@link BargeInType#hotword}. <code>null</code> if language 857 * should be reset to platform-specific default value for the 858 * prompts to be added. 859 */ 860 public void setHotWordBargeIn(BargeInType bargeInType) { 861 mBargeInType = bargeInType; 862 } 863 864 public List<? extends AudioItem> getAudioItems() { 865 return Collections.unmodifiableList(mAudioItems); 866 } 867 868 public String getLanguage() { 869 return mLanguage; 870 } 871 872 public SpeechRecognition getSpeechRecognition() { 873 return mSpeechRecognition; 874 } 875 876 public DtmfRecognition getDtmfRecognition() { 877 return mDtmfRecognition; 878 } 879 880 public BargeInType getBargeInType() { 881 return mBargeInType; 882 } 883 884 @Override 885 public JsonValue asJson() { 886 JsonObjectBuilder builder = JsonUtils.createObjectBuilder(); 887 JsonUtils.add(builder, LANGUAGE_PROPERTY, mLanguage); 888 JsonUtils.add(builder, BARGE_IN_TYPE_PROPERTY, mBargeInType == null ? null : mBargeInType.name()); 889 JsonUtils.add(builder, AUDIO_ITEMS_PROPERTY, JsonUtils.toJson(mAudioItems)); 890 JsonUtils.add(builder, DTMF_RECOGNITION_PROPERTY, mDtmfRecognition); 891 JsonUtils.add(builder, SPEECH_RECOGNITION_PROPERTY, mSpeechRecognition); 892 return builder.build(); 893 } 894 895 @Override 896 public int hashCode() { 897 final int prime = 31; 898 int result = 1; 899 result = prime * result + ((mAudioItems == null) ? 0 : mAudioItems.hashCode()); 900 result = prime * result + ((mBargeInType == null) ? 0 : mBargeInType.hashCode()); 901 result = prime * result + ((mDtmfRecognition == null) ? 0 : mDtmfRecognition.hashCode()); 902 result = prime * result + ((mLanguage == null) ? 0 : mLanguage.hashCode()); 903 result = prime * result + ((mSpeechRecognition == null) ? 0 : mSpeechRecognition.hashCode()); 904 return result; 905 } 906 907 @Override 908 public boolean equals(Object obj) { 909 if (this == obj) return true; 910 if (obj == null) return false; 911 if (getClass() != obj.getClass()) return false; 912 Prompt other = (Prompt) obj; 913 if (mAudioItems == null) { 914 if (other.mAudioItems != null) return false; 915 } else if (!mAudioItems.equals(other.mAudioItems)) return false; 916 if (mBargeInType != other.mBargeInType) return false; 917 if (mDtmfRecognition == null) { 918 if (other.mDtmfRecognition != null) return false; 919 } else if (!mDtmfRecognition.equals(other.mDtmfRecognition)) return false; 920 if (mLanguage == null) { 921 if (other.mLanguage != null) return false; 922 } else if (!mLanguage.equals(other.mLanguage)) return false; 923 if (mSpeechRecognition == null) { 924 if (other.mSpeechRecognition != null) return false; 925 } else if (!mSpeechRecognition.equals(other.mSpeechRecognition)) return false; 926 return true; 927 } 928 929 @Override 930 public String toString() { 931 return asJson().toString(); 932 } 933 } 934 935 /** 936 * Builder used to ease the creation of instances of {@link Interaction}. 937 * <p> 938 * Building an {@link Interaction} implies the following steps: 939 * <ul> 940 * <li>Add some prompts 941 * <ul> 942 * <li>For each prompt, speech/DTMF recognition can be specified (thus 943 * enabling <i>barge-in</i> when the prompt is played). 944 * </ul> 945 * <li>Once all prompts are added, optionally specify either: 946 * <ul> 947 * <li>a final recognition window (speech or DTMF) 948 * <li>a final recording window 949 * </ul> 950 * </ul> 951 * <p> 952 * At any time, it is possible to change the current language used for 953 * prompts (relevant to speech synthesis) and the barge-in type, i.e. 954 * <i>speech</i> or <i>hotword</i>. 955 * <p> 956 * This can be translated to: 957 * 958 * <pre> 959 * Builder builder = new Builder(); 960 * builder.addPrompt(...); 961 * builder.addPrompt(...); 962 * //... repeat as needed 963 * Interaction interaction = builder.build(...); 964 * </pre> 965 * 966 * @author Nu Echo Inc. 967 */ 968 public static final class Builder { 969 private final String mName; 970 private final List<Prompt> mPrompts = new ArrayList<Prompt>(); 971 972 private String mLanguage; 973 private BargeInType mBargeInType; 974 975 private boolean mBuilt; 976 977 public Builder(String name) { 978 mName = name; 979 } 980 981 /** 982 * Sets the barge-in type to either {@link BargeInType#speech} or 983 * {@link BargeInType#hotword} for the prompts that will be added using 984 * one of the <code>addPrompt(...)</code> methods. 985 * <p> 986 * Note: When a {@link Builder} is created, the default value for this 987 * flag is <code>null</code>, meaning the barge-in type will be 988 * platform-dependant. 989 * 990 * @param bargeInType 991 * <ul> 992 * <li>{@link BargeInType#speech} 993 * <li>{@link BargeInType#hotword} 994 * <li><code>null</code> reverts to platform default value 995 * </ul> 996 * @return this builder 997 */ 998 public Builder setBargeInType(BargeInType bargeInType) { 999 mBargeInType = bargeInType; 1000 return this; 1001 } 1002 1003 /** 1004 * Sets the language code for the prompts that will be added using one 1005 * of the <code>addPrompt(...)</code> methods. 1006 * <p> 1007 * Note: When a @{link Builder} is created, the default value for this 1008 * property is <code>null</code>, i.e. no explicit language code will be 1009 * generated in the VoiceXML thus relying on the platform-specific 1010 * default language code. 1011 * 1012 * @param language language code for this prompt. <code>null</code> if 1013 * language should be reset to platform-specific default 1014 * value for the prompts to be added. 1015 * @return this builder 1016 */ 1017 public Builder setLanguage(String language) { 1018 mLanguage = language; 1019 return this; 1020 } 1021 1022 /** 1023 * Adds a prompt with DTMF recognition only. 1024 * 1025 * @param dtmfRecognition configuration for the DTMF recognition 1026 * @param audioItems audio items to be played during this prompt. 1027 * @return this builder 1028 */ 1029 public Builder addPrompt(DtmfRecognition dtmfRecognition, AudioItem... audioItems) { 1030 return addPrompt(dtmfRecognition, null, asListChecked(audioItems)); 1031 } 1032 1033 /** 1034 * Adds a prompt with DTMF recognition only. 1035 * 1036 * @param dtmfRecognition configuration for the DTMF recognition 1037 * @param audioItems audio items to be played during this prompt. 1038 * @return this builder 1039 */ 1040 public Builder addPrompt(DtmfRecognition dtmfRecognition, List<? extends AudioItem> audioItems) { 1041 return addPrompt(dtmfRecognition, null, audioItems); 1042 } 1043 1044 /** 1045 * Adds a prompt with speech recognition only. 1046 * 1047 * @param speechRecognition configuration for the speech recognition 1048 * @param audioItems audio items to be played during this prompt. 1049 * @return this builder 1050 */ 1051 public Builder addPrompt(SpeechRecognition speechRecognition, AudioItem... audioItems) { 1052 return addPrompt(null, speechRecognition, asListChecked(audioItems)); 1053 } 1054 1055 /** 1056 * Adds a prompt with speech recognition only. 1057 * 1058 * @param speechRecognition configuration for the speech recognition 1059 * @param audioItems audio items to be played during this prompt. 1060 * @return this builder 1061 */ 1062 public Builder addPrompt(SpeechRecognition speechRecognition, List<? extends AudioItem> audioItems) { 1063 return addPrompt(null, speechRecognition, audioItems); 1064 } 1065 1066 /** 1067 * Adds a prompt with both DTMF and speech recognition. 1068 * 1069 * @param speechRecognition configuration for the speech recognition or 1070 * <code>null</code> to disable DTMF recognition. 1071 * @param dtmfRecognition configuration for the DTMF recognition or 1072 * <code>null</code> to disable DTMF recognition. 1073 * @param audioItems audio items to be played during this prompt. 1074 * @return this builder 1075 */ 1076 public Builder addPrompt(DtmfRecognition dtmfRecognition, 1077 SpeechRecognition speechRecognition, 1078 AudioItem... audioItems) { 1079 return addPrompt(dtmfRecognition, speechRecognition, asListChecked(audioItems)); 1080 } 1081 1082 /** 1083 * Adds a prompt with both DTMF and speech recognition. 1084 * 1085 * @param speechRecognition configuration for the speech recognition or 1086 * <code>null</code> to disable DTMF recognition. 1087 * @param dtmfRecognition configuration for the DTMF recognition or 1088 * <code>null</code> to disable DTMF recognition. 1089 * @param audioItems audio items to be played during this prompt. 1090 * @return this builder 1091 */ 1092 public Builder addPrompt(DtmfRecognition dtmfRecognition, 1093 SpeechRecognition speechRecognition, 1094 List<? extends AudioItem> audioItems) { 1095 Assert.noNullValues(audioItems, "audioItems"); 1096 Prompt prompt = new Prompt(speechRecognition, dtmfRecognition, audioItems); 1097 prompt.setLanguage(mLanguage); 1098 prompt.setHotWordBargeIn(mBargeInType); 1099 mPrompts.add(prompt); 1100 return this; 1101 } 1102 1103 /** 1104 * Adds a prompt without any DTMF nor speech recognition (no barge-in). 1105 * 1106 * @param audioItems audio items to be played during this prompt. 1107 * @return this builder 1108 */ 1109 public Builder addPrompt(AudioItem... audioItems) { 1110 return addPrompt(asListChecked(audioItems)); 1111 } 1112 1113 /** 1114 * Adds a prompt without any DTMF nor speech recognition (no barge-in). 1115 * 1116 * @param audioItems audio items to be played during this prompt. 1117 * @return this builder 1118 */ 1119 public Builder addPrompt(List<? extends AudioItem> audioItems) { 1120 Assert.noNullValues(audioItems, "audioItems"); 1121 Prompt prompt = new Prompt(audioItems); 1122 prompt.setLanguage(mLanguage); 1123 prompt.setHotWordBargeIn(mBargeInType); 1124 mPrompts.add(prompt); 1125 return this; 1126 } 1127 1128 /** 1129 * Builds the interaction. This method adds a DTMF recognition window 1130 * after the prompts. 1131 * 1132 * @param dtmfRecognition configuration for the DTMF recognition 1133 * @param noinputTimeout timeout value before a <code>noinput</code> is 1134 * generated. 1135 * @param acknowledgeAudioItems audioItems to be played upon recognition 1136 * @return the interaction 1137 */ 1138 public Interaction build(DtmfRecognition dtmfRecognition, 1139 Duration noinputTimeout, 1140 AudioItem... acknowledgeAudioItems) { 1141 return build(dtmfRecognition, null, noinputTimeout, asListChecked(acknowledgeAudioItems)); 1142 } 1143 1144 /** 1145 * Builds the interaction. This method adds a speech recognition window 1146 * after the prompts. 1147 * 1148 * @param speechRecognition configuration for the speech recognition 1149 * @param noinputTimeout timeout value before a <code>noinput</code> is 1150 * generated. 1151 * @param acknowledgeAudioItems audioItems to be played upon recognition 1152 * @return the interaction 1153 */ 1154 public Interaction build(SpeechRecognition speechRecognition, 1155 Duration noinputTimeout, 1156 AudioItem... acknowledgeAudioItems) { 1157 return build(null, speechRecognition, noinputTimeout, asListChecked(acknowledgeAudioItems)); 1158 } 1159 1160 /** 1161 * Builds the interaction. This method adds a DTMF recognition window 1162 * after the prompts. 1163 * 1164 * @param dtmfRecognition configuration for the DTMF recognition 1165 * @param noinputTimeout timeout value before a <code>noinput</code> is 1166 * generated. 1167 * @param acknowledgeAudioItems audioItems to be played upon recognition 1168 * @return the interaction 1169 */ 1170 public Interaction build(DtmfRecognition dtmfRecognition, 1171 Duration noinputTimeout, 1172 List<? extends AudioItem> acknowledgeAudioItems) { 1173 return build(dtmfRecognition, null, noinputTimeout, acknowledgeAudioItems); 1174 1175 } 1176 1177 /** 1178 * Builds the interaction. This method adds a speech recognition window 1179 * after the prompts. 1180 * 1181 * @param speechRecognition configuration for the speech recognition 1182 * @param noinputTimeout timeout value before a <code>noinput</code> is 1183 * generated. 1184 * @param acknowledgeAudioItems audioItems to be played upon recognition 1185 * @return the interaction 1186 */ 1187 public Interaction build(SpeechRecognition speechRecognition, 1188 Duration noinputTimeout, 1189 List<? extends AudioItem> acknowledgeAudioItems) { 1190 return build(null, speechRecognition, noinputTimeout, acknowledgeAudioItems); 1191 1192 } 1193 1194 /** 1195 * Builds the interaction. This method adds a speech and DTMF 1196 * recognition window after the prompts. 1197 * 1198 * @param speechRecognition configuration for the speech recognition 1199 * @param dtmfRecognition configuration for the DTMF recognition 1200 * @param noinputTimeout timeout value before a <code>noinput</code> is 1201 * generated. 1202 * @param acknowledgeAudioItems audioItems to be played upon recognition 1203 * @return the interaction 1204 */ 1205 public Interaction build(DtmfRecognition dtmfRecognition, 1206 SpeechRecognition speechRecognition, 1207 Duration noinputTimeout, 1208 AudioItem... acknowledgeAudioItems) { 1209 return build(dtmfRecognition, speechRecognition, noinputTimeout, asListChecked(acknowledgeAudioItems)); 1210 } 1211 1212 /** 1213 * Builds the interaction. This method adds a speech and DTMF 1214 * recognition window after the prompts. 1215 * 1216 * @param speechRecognition configuration for the speech recognition 1217 * @param dtmfRecognition configuration for the DTMF recognition 1218 * @param noinputTimeout timeout value before a <code>noinput</code> is 1219 * generated. 1220 * @param acknowledgeAudioItems audioItems to be played upon recognition 1221 * @return the interaction 1222 */ 1223 public Interaction build(DtmfRecognition dtmfRecognition, 1224 SpeechRecognition speechRecognition, 1225 Duration noinputTimeout, 1226 List<? extends AudioItem> acknowledgeAudioItems) { 1227 Assert.ensure(dtmfRecognition != null || speechRecognition != null, 1228 "Must provide at least one recognition configuration (speech or DTMF)"); 1229 1230 checkBuilt(); 1231 FinalRecognitionWindow finalRecognitionWindow = new FinalRecognitionWindow(dtmfRecognition, 1232 speechRecognition, 1233 noinputTimeout); 1234 1235 if (acknowledgeAudioItems != null) { 1236 finalRecognitionWindow.setAcknowledgeAudioItems(acknowledgeAudioItems); 1237 } 1238 1239 return new Interaction(mName, mPrompts, finalRecognitionWindow); 1240 1241 } 1242 1243 /** 1244 * Builds the interaction. This method adds a recording window after the 1245 * prompts. 1246 * 1247 * @param recording configuration for the recording 1248 * @param noinputTimeout timeout value before a <code>noinput</code> is 1249 * generated. 1250 * @param acknowledgeAudioItems audioItems to be played upon recording 1251 * completion 1252 * @return the interaction 1253 */ 1254 public Interaction build(Recording recording, Duration noinputTimeout, AudioItem... acknowledgeAudioItems) { 1255 return build(recording, noinputTimeout, asListChecked(acknowledgeAudioItems)); 1256 } 1257 1258 /** 1259 * Builds the interaction. This method adds a recording window after the 1260 * prompts. 1261 * 1262 * @param recording configuration for the recording 1263 * @param noinputTimeout timeout value before a <code>noinput</code> is 1264 * generated. 1265 * @param acknowledgeAudioItems audioItems to be played upon recording 1266 * completion 1267 * @return the interaction 1268 */ 1269 public Interaction build(Recording recording, 1270 Duration noinputTimeout, 1271 List<? extends AudioItem> acknowledgeAudioItems) { 1272 Assert.notNull(recording, "recording"); 1273 Assert.notNull(acknowledgeAudioItems, "acknowledgeAudioItems"); 1274 1275 checkBuilt(); 1276 FinalRecordingWindow finalRecordingWindow = new FinalRecordingWindow(recording, noinputTimeout); 1277 1278 if (acknowledgeAudioItems != null) { 1279 finalRecordingWindow.setAcknowledgeAudioItems(acknowledgeAudioItems); 1280 } 1281 1282 return new Interaction(mName, mPrompts, finalRecordingWindow); 1283 } 1284 1285 /** 1286 * Builds the interaction. This method does not allow any recording nor 1287 * recognition after the prompts. 1288 * 1289 * @return the interaction 1290 */ 1291 public Interaction build() { 1292 checkBuilt(); 1293 return new Interaction(mName, mPrompts); 1294 } 1295 1296 private void checkBuilt() { 1297 if (mBuilt) throw new IllegalStateException("build() method was already called."); 1298 mBuilt = true; 1299 } 1300 } 1301 1302}