Android and Speech Recognition (2)

In part 2 about speech recognition we do the reverse, instead od openly calling Google Search or integrating the speech recognition intent (prominently showing the Google logo/splash screen) we call the same recognition method in a headless way, making the experience more seamless. To make it menaingful in the aviation context, we will request BA flight information (through IAG Webservices) through audio.

(Please note, the below code snippets are incomplete and only highlighting the key methods, the complete sourcecode you find at Github, see link at the end of the post.)

Calling the speech recognizer

sr = SpeechRecognizer.createSpeechRecognizer(this);
sr.setRecognitionListener(new listener());

Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);

intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS,10);
sr.startListening(intent);

Implementation of the speech recognition class

class listener implements RecognitionListener
{
	public void onResults(Bundle results)
	{
		ArrayList data = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < data.size(); i++)
		{
			Log.d(TAG, "result " + data.get(i));
			result.append(i + ":" + data.get(i) + "\n");
		}
		textViewResult.setText(result);
	}
}

With this approach we get the usual 2 acoustic sounds signalling the begin of the speech recognition phase and the end (after a short time out).

If we need to create a hands-free user experience, avoiding the user to touch the screen, we will make use of the play or call button that you usually find on headsets. We can capture the event that gets fired when pressing these hardware buttons too.

Capture hardware button events

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
	Log.v(TAG, event.toString());
	if(keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode==KeyEvent.KEYCODE_MEDIA_PLAY){
		Log.i(TAG, event.toString());
		listenHeadless();
		return true;
	}
	return super.onKeyDown(keyCode, event);
}

Text-To-Speech

The missing link to the hands-in-the-pocket experience is the audio response by the app through our headset. We will add the standard Android Text-To-Speech (TTS) implementation.

textToSpeech = new TextToSpeech(getApplicationContext(),null);
...
textToSpeech.setPitch(1.0f);
textToSpeech.setSpeechRate(0.80f);
int speechStatus = textToSpeech.speak(textToSpeak, TextToSpeech.QUEUE_FLUSH, null, null);

if (speechStatus == TextToSpeech.ERROR) {
	Log.e("TTS", "Error in converting Text to Speech!");
}

A remark about text to speech engines, the default speech engines are not very pleasant to listen to, for an application speaking to users repeatedly or over a long time I recommend to look for commercial TTS engines (eg. Nuance, Cereproc,..). Check out the sound samples at the end of the post. TTS engines are usually produced per language. For a multi-lingual application you need to cater for every language.

Adding Webservice

To make this sample app more business relevant (airport related), we use the spoken text to request for flight data. For this purpose I will reuse the BA webservice call that was used in an earlier post. Do not forget to add permission for internet access to our app.

We straight away hit the first challenge here, we receive a text version of the spoken request. As we wont implement a semantic or contextual speech recognition we have to apply regular expression in order to attempt to find the key elements such as fligh number and date. The human requests the information in dozens of possible variations plus the varying interpretations by the speech-to-text engine, some listed below in the regex test screen (link).
To keep it simple we allow the user to request flight info for one particular British Airways flight number between yesterday and tomorrow. We will look for a 2 character 1..4 number combination in the text, plus using the keyword yesterday/tomorrow/today (or no statement representing today).

regular expression to identify flight number

To push the scenario further we can let the TTS engine read the response to the user, after selecting relevant information from the JSON formatted WS response.

Soundsamples

Sourcecode

References

Aviation API – Airline and Airport Webservices

The term API (Application programming interface) is not new, basically a defined access to a set of methods, subroutines and data-types made available by one component/service to be used/consumed by another component or application. You can think of API as the car manual and the library being the engine under the hood.

Since the early days of tinkering with the Windows DLL hell more than 20 years have passed, we have better tools and standards by now. In the space of native apps for Windows, Android and iOS we still work with libraries and SDK’s (still can be challenging when resolving dependencies and deployment).
In the world of web applications today we look mostly at RESTful Webservices responding in JSON or XML, a rather straight forward implementation, the only complexity depends on the authorization mode or the mapping of attributes. A lot of websites, portals, services or products expose their WS for usage by third parties, from Salesforce, Ebay, Amazon to Twitter and infinite more. While these mentioned samples operate independently or work as standalone services there is not much need for standardization of the payload, aka the structure of attributes, naming conventions etc. For others business domain there is a need of standardization of such, despite the availability of AIDX, AIDM and a few more data exchange models, there is no standard widely used in the (public) WS space in aviation.
I have to highlight in ACI ACRIS a Semantic Model is being actively developed and the Open API Shop exists as project as well.

I researched what webservices are currently offered to the public by airports and airlines, excluding the API’s of system vendors and travel platforms.
So far I found these webservices (last update 2018-07-06):

Airline

International Airline Group
British Airways, Iberia, Avios
https://developer.iairgroup.com/
Lufthansa https://developer.lufthansa.com/
Airfrance, KLM https://developer.airfranceklm.com/
Alaska Air
https://developer.alaskaair.com/
Transavia https://developer.transavia.com/
FlyDubai https://developer.flydubai.com/
Virgin Australia
https://developer.virginaustralia.com/
Ryanair https://developer.ryanair.com/
Turkish Airlines
https://developer.turkishairlines.com

Airports

Schiphol Airport
https://developer.schiphol.nl/
San Francisco Airport
https://www.flysfo.com/api
Frankfurt Airport
https://developer.fraport.de/
Svedavia Airports
https://apideveloper.swedavia.se/

Others

FAA US
https://app.swaggerhub.com/apis/FAA/ASWS/1.1.0
Flightaware
http://flightaware.com/commercial/flightxml

The usage terms and price models vary but basically all give some kind of developer access to evaluate the services and data at no cost.

To compare a few of the services using a flight status related call, omitting the authentication. The lack of standard, putting aside JSON response format and date format, is quite obvious. It might not be relevant in the space of individual apps to have the same response format, but if you want to combine data from various sources you have to handle the formats separately. Even the request format with the query parameter differs.

British Airways

https://api.ba.com/rest-v1/v1/flights;departureLocation=FRA;startTime=06:00;endTime=11:00

ba

Swedavia Airports

https://api.swedavia.se/flightinfo/v2/query?filter=airport eq ‘ARN’ and scheduled eq ‘180713’ and flightType eq ‘A’ and flightId eq ‘DY4572’

IMG122

Lufthansa

https://api.lufthansa.com/v1/operations/flightstatus/LH778/2018-07-13

IMG123

I also tried with RyanAir (still waiting for approval to get api key), Turkish Airlines (no flight status API), Schiphol (no webservice test available on the website).

Image: Creative Commons, Robert Yarnall Richie Photograph Collection, “Models with Oldsmobile Automobile, Lockheed 10B Electra, Delta Air Lines, 1940”

Build a RESTful Webservice in less than 5 minutes

There are quite some tutorials around about building and exposing a RESTful Webservice, but some of them are outdated and make you wade through complex dependencies and tinkering with deployment descriptors and web.xml files. But using RESTeasy, the JBoss implementation that is fully compliant with the JAX-RS 2.0 JCP specification, and Eclipse you can build a simple webservice (“hello world”) with less than 10 lines of sourceode with annotations and no web.xml used in a few minutes and run it on Wildfly.

Lets build a webservice that creates random numbers.

Continue reading

Create a JIRA client application with Netbeans

I want to embed the creation of JIRA issues into my web application. There are 2 option, as XML-RPC or SOAP client. I decide for the latter one as being the more-up-to-date technology.

Using Netbeans it is usually quite simple to create a WS client, just give the WSDL address and NB will create all required code for you. Btw, you need to install the JAX-RPC plugin, NB out-of-the-box only supports JAX-WS.
Add the “latest” build to your Netbeans plugin settings and you will find the plugin. (http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastStableBuild/artifact/nbbuild/nbms/updates.xml.gz)

Update Center

JAX-RPC Webservices

But in the case of the JIRA WSDL (you can test with http://jira.atlassian.com/rpc/soap/jirasoapservice-v2?wsdl or your own JIRA server) it fails.
While creating the WS client code it duplicates a number of variables.

/media/NetbeansProjects/Webservices/JiraDemo/build/generated-sources/jax-rpc/jiraws/JiraSoapService_deletePermissionScheme_Fault_SOAPSerializer.java:30: ns1_fault_QNAME is already defined in jiraws.JiraSoapService_deletePermissionScheme_Fault_SOAPSerializer
private static final javax.xml.namespace.QName ns1_fault_QNAME = new QName("http://xxxxxxx/rpc/soap/jirasoapservice-v2", "fault");

Errors

My temporary workaround: I create the Java application in Eclipse, add the WS there and import the project afterwards into NB. This works, but you loose the extra functions to handle WS in Netbeans. So on the long way, I hope someone can fix the plugin.

JIRA WS in Eclipse

JIRA WS in Eclipse

JIRA WS in Netbeans

WS Client

I recommend playing with the sample code from below references.

References: