Skip to content

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.ptvag.navigation.app";
    String serviceName = "com.ptvag.navigation.ri.RIService";
    Intent intent = new Intent();
    intent.setClassName(componentName, serviceName);
    boolean rc = getApplicationContext().bindService(intent,
    serviceConnection, Context.BIND_AUTO_CREATE);
When the binding succeeded, the service will call onServiceConnected() from the ServiceConnection we implemented above and set the Messenger "serviceMessenger", so from now on, we can send messages to the service.

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:

class IncomingHandler extends Handler
{
    @Override
    public void handleMessage(Message msg)
    {
        // check, if msg.arg1 != RI_ERROR_NOT_INITIALIZED
        if(!checkInitialized(msg))
            return;

        // handle the received message (here, we react on the MSG_RI_QUERY_MAP_POSITION message):
        switch (msg.what)
        {
            case Constants.MSG_RI_QUERY_MAP_POSITION:
            {
                Bundle bundle = msg.getData();
                // the PTV Navigator will put the error code in the arg1 field of the message:
                switch (msg.arg1)
                {
                    // message was successfully processed:
                    case Constants.RI_ERROR_NONE:
                        textOutput.append("Position: " + bundle.getInt("PositionMercX") + ";" + bundle.getInt("PositionMercY") + "\n");
                        textOutput.append("Scale: " + bundle.getInt("Scale") + "\n");
                        textOutput.append("Orientation: " + bundle.getInt("Orientation") + "\n\n");
                    break;
                    // no results found
                    case Constants.RI_ERROR_NO_RESULT_FOUND:
                        textOutput.append("No active map to query found.\n\n");
                    break;
                    default:
                        textOutput.append("Unexpected error.\n\n");
                    break;
                }
            }
            break;
            default:
                super.handleMessage(msg);
        }
    }
}

Types of messages

There are two main 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 Geocode a given address (retrieve the position). This data is attached as a Bundle to the message.

  • Messages which tell the PTV Navigator that the client wants to listen to certain events (like gps information). After sending such a message (like RegisterEventInformation), the PTV Navigator will push frequently messages of a special type. TO let the service know what kind of event the client wants to listen to, the message sender has to set the field "EventType" of the sending bundle to the type he wants frequently information for. See Event Types for a list of available events.

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.

Auto start up of the PTV Navigator

To communicate with the PTV Navigator, it is necessary that it's started and running. If the PTV Navigator is not running, it will as default try to start up when an RI command was sent to it (on some devices, this auto startup is not possible because of the device configuration). During start up, the PTV Navigator will return the error message RI_ERROR_IS_INITIALIZING until the application is fully running. This auto startup can be configured by the preference key "system_allow_start_over_ri" of the PTV Navigator. The default is set to auto start up, to disable it, set the key to false. When auto startup is disabled, the PTV Navigator will return RI_ERROR_NOT_INITIALIZED. In this case you have to start the PTV Navigator manually.

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_QUERY_PROFILES, 0, 0);
msg.replyTo = clientMessenger;
Bundle bundle = new Bundle();
bundle.putString("Profile", "Truck16");
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.

Push service commands

Clients can register themselves to certain events. After this registration, the PTV Navigator will frequently send information about these events. If the client wants to stop listening, it can send an unregister message. It's possible to listen to multiple events, just send a register message for all events you want to listen to. Let's show how this registering works by the following example: We want to listen to navigation information (which only works then a navigation is running and the GPS settings in the PTV Navigator are set to "GPS active in the background"). So the first thing we have to do is sending a message with the appropriate fields set to let the service know what type of information we want to receive. This is done by creating a bundle with the key EventType and the value of the wanted event, in our case EVENT_NAVIGATION_INFO.

Message msg = Message.obtain(null, Constants.MSG_RI_REGISTER_EVENT_LISTENER, 0, 0);
msg.replyTo = clientMessenger;
Bundle bundle = new Bundle();
bundle.putInt("EventType", Constants.EVENT_NAVIGATION_INFO);
msg.setData(bundle);
serviceMessenger.send(msg);

From know on, the PTV Navigator will send back frequently navigation information. The sent messages have the what field of the message set to MSG_RI_EVENT_DATA to inform the client that this message is a push message with event information. The message will be received in the IncomingHandler like in the examples above:

class IncomingHandler extends Handler
{
    @Override
    public void handleMessage(Message msg)
    {
        if(!checkInitialized(msg))
            return;
        switch (msg.what)
        {
            case Constants.MSG_RI_EVENT_DATA:
            {
                if(msg.arg1 == Constants.RI_ERROR_NONE) // no error
                {
                    Bundle bundle = msg.getData();
                    int eventType = bundle.getInt("EventType");

                    if (eventType == Constants.EVENT_NAVIGATION_INFO)
                    {
                        long timeToDestination = bundle.getLong("TimeToDestination");
                        // ... do something with the navi info...
                    }
                }
                else
                {
                    textOutput.append("Error\n");
                }
            }
            break;
            default:
                super.handleMessage(msg);
        }
    }
}

If the client doesn't want to listen to the information anymore, he can unregister the service:

Message msg = Message.obtain(null, Constants.MSG_RI_UNREGISTER_EVENT_LISTENER, 0, 0);
msg.replyTo = clientMessenger;
Bundle bundle = new Bundle();
bundle.putint("EventType", Constants.EVENT_NAVIGATION_INFO);
msg.setData(bundle);
serviceMessenger.send(msg);

We create a message with the id MSG_RI_UNREGISTER_EVENT_LISTENER and set the EventType of the bundle to EVENT_NAVIGATION_INFO to unregister the listening to navigation info.

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.