Tutorial
The tutorial will show you how to integrate the remote interface in your application, how to establish a connection to the PTV Navigator and how to communicate with the PTV Navigator. We will show how messages will be created, how to send them to the PTV Navigator and how to receive messages sent by the PTV Navigator. Remark: We often use constant values for the message IDs, the type of messages and the error codes. All these values are stored in the "Constants" class of the RITest demo program. You could also use the integer values given by this documentation, but for better readability, we prefer to use the values of the Constants class.
Establishing a connection to the PTV Navigator service (from an existing android activity)
There are 3 steps to connect to the navigation service:
Create a service connection
First of all, instantiate a new ServiceConnection. This class handles the interaction with the interface of the service, and therefore we have to create a new one and overwrite two methods:
private Messenger serviceMessenger; // with this messenger, your app can send messages to the PTV Navigator private Messenger clientMessenger; // with this messenger, the PTV Navigator can send messages to you protected ServiceConnection serviceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. serviceMessenger = new Messenger(service); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. serviceMessenger = null; } };
In the onServiceConnected() method, the Messenger "serviceMessenger" will be set when the service binds to it. It is our data highway for sending messages from the client to the PTV Navigator. The onServiceDisconnected() method will be called when the service disconnects, mostly then the process that hosts the service crashes. The Messenger "clientMessenger" will be used later on to let the PTV Navigator send back messages to the client. The two Messenger objects "serviceMessenger" and "clientMessenger" will be used to do the actual sending of messages between the PTV Navigator and the client and so, they will be heavily used later on.
Bind to the service
This is done by creating an intent with the correct class name, and calling getApplicationContext().bindService(). The class name consists of the package name of the application which provides the service (componentName) and the package name of the service (serviceName). The package name of the service is always "com.ptvag.navigation.ri.RIService", the componentName may be different, please see the example code in the RITest application for getting the right name.
String componentName = "com.ptv.navigation.app"; String serviceName = "com.ptv.navigation.ri.RIService"; Intent intent = new Intent(); intent.setClassName(componentName, serviceName); boolean rc = getApplicationContext().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
Receive messages from the service
To receive messages from the service, we have to implement a so called Handler and overwrite the method handleMessage() which will be called everytime a message arrives:
createClientMessenger(new IncomingHandler() { @Override public void handleMessage(Message msg) { if(!checkInitialized(msg)) return; // for every message we receive, we will put some text in the // textOutput edit field Bundle bundle = msg.getData(); bundle.setClassLoader(Station.class.getClassLoader()); int errorType = msg.arg1; switch (msg.what) { case Constants.MSG_RI_ADD_STATION: textOutput.append("Add station\n"); break; default: super.handleMessage(msg); } } } public void createClientMessenger(Handler handler) { clientMessenger = new Messenger(handler); }
Types of messages
- Messages which will trigger immediately an action in the PTV Navigator These are messages like Start a navigation or Set an existing vehicle profile as active. The client sends the message to the PTV Navigator, and the PTV Navigator will immediately trigger the action. Some of these messages needs data to be sent to the PTV Navigator like retrieving the current active profile. This data is attached as a Bundle to the message.
- Messages which will trigger an action with a long duration like starting a navigation. In these cases, the navigator will return the message RI_ERROR_LONGTIME_OPERATION_IN_PROGRESS when the same message or a similar one which would intercept the current long lasting operation is triggered. Please wait until the PTV Navigator returns the success message for the sent message before calling it again.
All messages have in common that the PTV Navigator sends back an acknowledgement message with an error code in the field "arg1" of the message. The user has to check every time this acknowledgement message and it'S returning error code to know that the command sent to the PTV Navigator had succeeded or not.
For more details, see API-Reference.
Start up of the PTV Navigator
To communicate with the PTV Navigator, it is necessary that it's started and running.
Sending a message
First, be sure that the PTV Navigator software is running and the binding had suceeded (the onServiceConnected() method from the ServiceConnection had been called).
Example
Let's do a little practice: we want to query all vehicle profiles that are available in the PTV Navigator. To see what we have to do and what message has to be sent, take a look at the documentation for it: Get all available vehicle profiles. Therefore the first thing we have to do to send a message is creating a Message object by obtaining it from the Android message pool and set the correct message id for what you want to do:
Message msg = Message.obtain(null, Constants.MSG_RI_QUERY_PROFILES, 0, 0);
Next, set the replyTo field of the Message to "clientMessenger", so the PTV Navigator can send messages back to us:
msg.replyTo = clientMessenger;
Finally, send the message with the serviceMessenger object to the PTV Navigator:
serviceMessenger.send(msg);
Sending a message with data is similar to the above described, you only have to attach the bundle to the message, so the code looks like this:
Message msg = Message.obtain(null, Constants.MSG_RI_ADD_STATION, 0, 0); msg.replyTo = getClientMessenger(); Bundle bundle = new Bundle(); bundle.putString("Name", textName.getText().toString()); bundle.putDouble("Longitude", 8.403); bundle.putDouble("Latitude", 49.00); msg.setData(bundle); serviceMessenger.send(msg);
Receiving messages
When the PTV Navigator receives a message from a client, it will immediately send back a message with an error code, and optionally a bundle of data. To receive messages from the PTV Navigator, you have to implement a Handler (see above). When the PTV Navigator sends back a message, the handleMessage() method of the handler will be called, and the client can react to the message. The message arrived has a field "arg1" which holds the actual errorcode, set by the PTV Navigator, to let the client determine if the command succeeded or not. See Error Values for more details. The message can also hold data as a Bundle. Here is how to handle the incoming message from the PTV Navigator in the Handler and how to get the information out of the message in our example of getting the vehicle profiles:
class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { if(!checkInitialized(msg)) return; // for every message we receive, we will put some text in the // textOutput edit field switch (msg.what) { case Constants.MSG_RI_QUERY_PROFILES: { if(msg.arg1 == Constants.RI_ERROR_NONE) // no error { Bundle bundle = msg.getData(); textOutput.append("Available profiles:\n"); String[] profiles = bundle.getStringArray("Profiles"); // read out the profiles if (profiles != null && profiles.length > 0) { for (int i=0; i<profiles.length; i++) { textOutput.append(profiles[i] + "\n"); } textProfile.setText(profiles[new Random().nextInt(profiles.length)]); } } else // error { textOutput.append("Error\n"); } } break; default: super.handleMessage(msg); } } }
As you can see, the message arrives in the incoming handler and we only have to test if the message is the one we want. We do this by checking the what
-field of the message which holds the message id. After that, we can read the data out of the Bundle.
We also checked the arg1
-field for an error code, to see if the command succeeded.
(The checkInitialized() function in the above example does only check, if the PTV Navigator is running by testing the error code for RI_ERROR_NOT_INITIALIZED
.)
Reading out a Bundle from a message is generally done by this code sequence:
Bundle bundle = msg.getData();
The Bundle stores key/value pairs, so to read a key with name "Profiles" of type StringArray, you can call:
String[] profiles = bundle.getStringArray("Profiles")
In the documentation of the various messages, you will find every key you can set and get when sending or receiving messages with bundles. Take a look at the Android Bundle documentation for more details about Bundles and Messages.
Restrictions when sending commands
Some of the commands cannot be sent in particular situations, like deleting the target station list while navigating, please check the API reference for more details.
Next steps
To find out more, see the RITest page where you can find the RITest documentation.