TransFollow Android app intents

1. Introduction

Android app developers and board computer developers can integrate TransFollow by connecting to the TransFollow Android App. The TransFollow app on Android provides an integration interface, through the use of intents, to allow third-party app developers to integrate TransFollow functionality in their applications. The majority of these calls require the user to be authenticated using a TransFollow account.

Every intent is supported through Activity intent usage, however a subset of the intents can also be used through the use of our intent service. The service requires some extra setting up to use, but with service usage no TransFollow user interface will pop up. You can find more information about the intent service here.

This page first describes every intent we support (See Interface Specification), after which it will describe the differences in usage of the intent service.

The following is a quick access list to the supported intent calls:

Authentication Calls Viewing Calls Freight Document Adjusting Calls Misc Calls
Login Show Sign Poll
Logout Show RTIS Add Comment Preload
Show PDF Update Arrival/Departure Times Launcher Icon Visibility
Show for Legal Compliance Update Structured Goods Toggle Push Notifications
Show freight document PDF QR code Sign Multiple Freight Documents
Counterparty Signing
Update Mileage
The Miscellaneous result codes should also be kept in mind when integrating with the intent calls.

Distribution

The public TransFollow app is available on the Google Play Store.

We distribute a separate version of the app for development and testing purposes in our partner environment. Please send an email to support@transfollow.org to request access to the partner version of the app.

In this document we will address differences per environment if applicable.

2. User Authentication - Oauth2

The session that the TransFollow app uses to maintain an authorised connection with the backend has an access token and a refresh token. The access token is valid for a maximum of 10 minutes, the refresh token is valid for 1 month. Whenever the access token expires, the TransFollow component will automatically request a new access token using the refresh token that is still valid. Upon return the TransFollow app will have a new access token and a new refresh token.

In other words, as long as the TransFollow app is used at least 1x per month, the app can be used without the user having to log in again.

If, for some reason, the refresh token is not valid anymore, the caller of the TransFollow app will get a 1002 (INVALID_CREDENTIALS). This can happen with every TransFollow app intent call. After this happens the caller can show a login screen to the user, login into TransFollow app and retry the action.

This can be tested by logging into the TransFollow app and then requesting a "reset password" via the portal: https://portal.partner.transfollow.com That will invalidate both the active access token and the refresh token.

3. Interface Specification

The interface of the TransFollow app consists of an Android Activity which can be started using an Intent.

3.1 Starting the TF Android app via intents

The TransFollow app can be started using an explicit Intent. The target app package name and activity class name of the Intent should be defined as follows:

Property Value
App package name (production) com.transfollow.tf
App package name (partner) com.transfollow.tf.partner
Activity class name com.transfollow.tf.routing.RoutingActivity

To run the TransFollow app you need to provide a call, specified by the Intent action. Several calls require parameters that can be supplied through Intent extras. Every executed call will return a result code.

TransFollow app will send a result code for all performed calls. In order to be able to receive these result codes you need to start the RoutingActivity using Context.startActivityForResult().

In the following paragraphs the available calls will be described. First the login call will be presented, followed by an example of implementation, and then finally the other available intents.

3.2 LOGIN call

The LOGIN call can be used to log into the TransFollow API. Any following calls will be authenticated using the credentials provided in this call.

The Intent action name is com.transfollow.tf.intent.action.LOGIN and it accepts the following parameters:

Extra Name Type Required Description
com.transfollow.tf.intent.extras.USERNAME String yes The TransFollow account username
com.transfollow.tf.intent.extras.PASSWORD String yes The TransFollow account password
com.transfollow.tf.intent.extras.RECEIVE_PUSH_NOTIFICATIONS boolean no The value is true by default. Whether to receive push notifications from TransFollow. If you set it to false TransFollow app will not register for push notifications and you won't receive them.

The LOGIN call can return the following result codes:

Result Code Short Description Description
1001 SUCCESS Successfully logged in
1002 INVALID_CREDENTIALS The user is not logged in or the session has expired
1003 USERNAME_PARAMETER_NOT_PROVIDED The login call was performed without the required username parameter
1004 PASSWORD_PARAMETER_NOT_PROVIDED The login call was performed without the required password parameter

3.3 Example Implementation

You can use the code examples below to get you started.

Starting the RoutingActivity

You can start the TransFollow Partner app activity through an intent:

final Intent intent = new Intent();

final String username = "user@test.tld";
final String password = "myverysafepassword";

intent.setClassName("com.transfollow.tf.partner", "com.transfollow.tf.routing.RoutingActivity");

// Check if the TransFollow app is installed on the current device
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) == null) {
  // Component is not installed, notify the user
  AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
    builder.setTitle("Error");
    builder.setMessage("TransFollow app is not installed");
    builder.create().show();
} else {
  intent.setAction("com.transfollow.tf.intent.action.LOGIN")
  intent.putExtra("com.transfollow.tf.intent.extras.USERNAME", username);
  intent.putExtra("com.transfollow.tf.intent.extras.PASSWORD", password);

  // Start the Login intent using an arbitrary request code
  startActivityForResult(intent, 0);
}

Processing the resultcode

Override the Activity.onActivityResult() method to handle the result. The resultCode parameter contains the returned result code from the TransFollow app.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  // handle result code
}

Logging in, including processing the response from the interface, goes like this:

public class MainActivity {

    TextView t = null;
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        switch(resultCode) {
            case 1001:
                t.setText("Succesfully logged in");
                break;
            case 1002:
                t.setText("The user is not logged in");
                break;
            default:
                t.setText("Something else was wrong");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        t = (TextView)findViewById(R.id.tv);

        final Intent intent = new Intent();

        final String email = "dev@example.tld";
        final String pwd = "password";

        intent.setClassName("com.transfollow.tf.partner", "com.transfollow.tf.routing.RoutingActivity");

        // Check if the TransFollow app is installed on the current device
        if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) == null) {
            // Component is not installed, notify the user
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setTitle("Error");
            builder.setMessage("TransFollow app is not installed");
            builder.create().show();
        } else {
            intent.setAction("com.transfollow.tf.intent.action.LOGIN");
            intent.putExtra("com.transfollow.tf.intent.extras.USERNAME", email);
            intent.putExtra("com.transfollow.tf.intent.extras.PASSWORD", pwd);

            // Start the Login intent using an arbitrary request code
            startActivityForResult(intent, 0);
        }
    }
}

3.4 SHOW call

The SHOW call can be used to show a freight document in full.

The Intent action name is com.transfollow.tf.intent.action.SHOW and it accepts two parameters:

Extra Name Type Required Default Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_ID String yes N/A Freight Document ID of the freight document you want to show
com.transfollow.tf.intent.extras.SHOW_SIGN_BUTTON Boolean no false Show a "Sign" button in the freight document viewing screen

The SHOW call can return the following result codes:

Result Code Short Description Description
2001 FREIGHT_DOCUMENT_FOUND The freight document was found
2002 FREIGHT_DOCUMENT_NOT_FOUND The freight document was not found (or the user does not have access to it)
2003 FREIGHT_DOCUMENT_ID_PARAMETER_NOT_PROVIDED The required freight document id was not provided

Note: If the SHOW_SIGN_BUTTON parameter is true, then the result codes for the sign call can also be returned.

3.5 SIGN call

The SIGN call can be used to start the signing process of a specified freight document.

The Intent action name is com.transfollow.tf.intent.action.SIGN and it requires the following parameters:

Extra Name Type Required Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_ID String Yes Freight Document ID of the freight document for which you want to start the signing process
com.transfollow.tf.intent.extras.DRIVER_NAME String No The name of the driver to add to the signature
com.transfollow.tf.intent.extras.TRANSFER_TYPE String No The transfer type to perform if known beforehand. This allows to bypass the transfer type selection screen. Valid values are: COLLECTION, CARRIER_TO_CARRIER, DELIVERY
com.transfollow.tf.intent.extras.APPROVAL_TYPE String No The approval type to perform if known beforehand. This allows to bypass the approval type selection screen. Valid values are: TFA, SIGN_ON_GLASS, OWN

The SIGN call can return the following result codes:

Result Code Short Description Description
3001 USER_CANCELLED_SIGNING The user has cancelled the signing process
3002 CANNOT_SIGN_DRAFT_OR_CANCELED The freight document cannot be signed, because it has status 'draft' or 'cancelled'
3003 CANNOT_SIGN_DELIVERED The freight document cannot be signed, because it has the status 'delivered'
3004 Reserved
3005 DID_APPROVE The user has successfully signed the freight document
3006 DID_NOT_APPROVE The user has not signed the freight document
3007 NO_SUBMIT_APPROVAL_FOR_OTHER_PARTY_PERMISSION The user does not have permission to sign the freight document on behalf of another party
3008 DID_APPROVE_VIA_SIGN_ON_GLASS The user has signed the freight document using sign on glass
3009 UNKNOWN_TRANSFER_TYPE The transfer type provided is not one of the required values
3010 TRANSFER_TYPE_NOT_ALLOWED The transfer type provided is not allowed at this point e.g. picking up at COLLECTION if this transfer type already happened
3011 UNKNOWN_APPROVAL_TYPE The approval type provided is not one of the required values

3.6 SIGN_MULTIPLE call

The SIGN_MULTIPLE call can be used to start the signing process for multiple specified freight documents (up to a maximum of 20 freight documents).

The Intent action name is com.transfollow.tf.intent.action.SIGN_MULTIPLE and it requires the following parameters:

Extra Name Type Required Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_IDS ArrayList<String> Yes List of Freight Document IDs of the freight documents for which you want to start the signing process
com.transfollow.tf.intent.extras.DRIVER_NAME String No The name of the driver to add to the signature

The SIGN_MULTIPLE call can return the following result codes:

Result Code Short Description Description Applicable Result Data Keys
14001 EXCEEDED_MAXIMUM_AMOUNT More than 20 freight document ids were provided
3001 USER_CANCELLED_SIGNING The user has cancelled the signing process
3002 CANNOT_SIGN_DRAFT_OR_CANCELED Signing cannot be initiated because one or more of the freight documents have the status 'draft' or 'cancelled' freightDocumentsForWhichErrorOccurred
3003 CANNOT_SIGN_DELIVERED Signing cannot be initiated because one or more of the freight documents have the status 'delivered' freightDocumentsForWhichErrorOccurred
3004 Reserved
3005 DID_APPROVE The user has successfully signed at least one freight document signedFreightDocuments, unsignedFreightDocuments
3006 DID_NOT_APPROVE The user has not signed any freight document
3007 NO_SUBMIT_APPROVAL_FOR_OTHER_PARTY_PERMISSION The user does not have permission to sign one or more freight documents on behalf of another party freightDocumentsForWhichErrorOccurred
3008 DID_APPROVE_VIA_SIGN_ON_GLASS The user has signed the freight documents using sign on glass
6002 FREIGHT_DOCUMENT_IDS_PARAMETER_NOT_PROVIDED No freight document ids were provided

Additionally the SIGN_MULTIPLE call can also return additional information when returning a result code:

Result Data Key Type Description
signedFreightDocuments ArrayList<String> The freight document ids of the freight documents that were signed
unsignedFreightDocuments ArrayList<String> The freight document ids of the freight documents that were not signed
freightDocumentsForWhichErrorOccurred ArrayList<String> The freight document ids that were provided in the intent call, for which the error result code occurred. In addition to the table above, it can also occur for result code 2002.

This is returned in the data parameter in the onActivityResult function when one of the results codes above is returned. The following is an example of how you can retrieve the result data:

public class MainActivity {
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        switch(resultCode) {
            case 3005:
                List<String> signedFreightDocumentIds = data.getStringArrayListExtra("signedFreightDocuments");
                List<String> unsignedFreightDocumentIds = data.getStringArrayListExtra("unsignedFreightDocuments");
                break;
            case 2002:
                List<String> freightDocumentsUserCannotAccess = data.getStringArrayListExtra("freightDocumentsForWhichErrorOccurred");
                break;
            case 3007:
                List<String> freightDocumentsUserMayNotSignForOthers = data.getStringArrayListExtra("freightDocumentsForWhichErrorOccurred");
                break;
            case 3003:
                List<String> signedFreightDocumentIds = data.getStringArrayListExtra("signedFreightDocuments");
                // signedFreightDocumentIds is null because  no signing took place
                break;
            case 3006:
                List<String> signedFreightDocumentIds = data.getStringArrayListExtra("signedFreightDocuments");
                List<String> unsignedFreightDocumentIds = data.getStringArrayListExtra("unsignedFreightDocuments");
                // both variables are null because the result code indicates that nothing was signed
                break;
            default:
                break;
        }
    }
  }

3.7 COUNTERPARTY_SIGNING call

The COUNTERPARTY_SIGNING call can be used to start the signing process when taking over goods from the carrier.

The COUNTERPARTY_SIGNING call can return the following result codes:

Result Code Short Description Description Applicable Result Data Keys
3001 USER_CANCELLED_SIGNING The user has cancelled the signing process
3005 DID_APPROVE The user has access to all scanned freight documents accessibleFreightDocuments
3011 DID_APPROVE_NOT_ALL_ACCESSIBLE The user does not have access to one or more freight documents. Only the scanned freight documents the counterparty has access to is returned. accessibleFreightDocuments

The COUNTERPARTY_SIGNING call can also return additional information when returning a result code:

Result Data Key Type Description
accessibleFreightDocuments ArrayList<String> The scanned freight document ids the counterparty has access to

3.8 LOGOUT call

The LOGOUT call can be used to log out the single user that can be logged in.

The Intent action name is com.transfollow.tf.intent.action.LOGOUT and it doesn't accept any parameters. Since there is no possibility to be logged in with multiple accounts there is no need to specify the account you want to log out.

The LOGOUT call can return the following result codes:

Result Code Short Description Description
4001 SUCCESS The user was successfully logged out
4002 FAILED_QUEUE_NOT_EMPTY The user cannot log out as there are unprocessed freight document changes

A logout can fail when the queue is not empty.

3.9 POLL call

Certain actions performed by the TransFollow app, such as signing a freight document, are synchronised with the TransFollow backend asynchronously. The TransFollow app maintains a queue of deferred network operations which persists beyond the application lifecycle (app kills, crashes). The POLL call is used to ascertain that all actions have been synchronised with the backend. The Intent action name is com.transfollow.tf.intent.action.POLL. The result code QUEUE_EMPTY signifies that there are no more actions to be synchronised. This call does not accept any parameters.

The POLL call can return the following result codes:

Result Code Short Description Description
5001 QUEUE_EMPTY All the commits have been processed
5002 QUEUE_NOT_EMPTY There are still unprocessed commits

3.10 PRELOAD call

With the preload call it is possible to make a selection of freight documents available for offline usage. The TransFollow app will always - online or offline - try to download the latest freight document. If the device may encounter an offline situation existing freight document with attachments can be preloaded by supplying an array of existing freightDocumentId's. If the device is in the field, the app will first try to download a fresher version online and when that fails, use the offline version. This way it can still show the latest known contents of the freight document, including attachments. These documents can also be signed offline.

If the user has no rights to one or more of the freight documents in the parameter list these documents will not be in the resulting set. There will however be no error notice indicating a lack of rights. The preloading of documents will be performed in the background, however the UI will still be visible.

Extra Name Type Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_IDS String[] An array of Freight Document ids to preload.

The PRELOAD call can return the following result codes:

Result Code Short Description Description
6001 SUCCESS Successfully preloaded the freight documents
6002 FREIGHT_DOCUMENT_IDS_PARAMETER_NOT_PROVIDED No freight document ids were provided
6003 PRELOAD_CANCELLED Preloading was cancelled

3.11 ADD_COMMENT call

With the ADD_COMMENT call it is possible to place comment on a specific freight document.

The Intent action name is com.transfollow.tf.intent.action.ADD_COMMENT and it accepts the following parameters:

Extra Name Type Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_ID String Freight Document ID of the freight document for which you want to add the comment
com.transfollow.tf.intent.extras.COMMENT_TEXT String The comment text
com.transfollow.tf.intent.extras.ATT_NAME String[] A list of attachment names as the should appear in the comment.
com.transfollow.tf.intent.extras.ATT_ORIGINAL_FILENAME String[] A list of attachment original file names as known to the sender.
com.transfollow.tf.intent.extras.ATT_CONTENT String[] A list of attachment path names to files on external storage. The TransFollow app must be able to read the files.
com.transfollow.tf.intent.extras.ATT_MIME_TYPE String[] A list of attachment mime types belonging to the attachments.

In order to attach files to a comment the arrays ATT_NAME[], ATT_ORIG_NAME[], ATT_CONTENT[] and ATT_MIME_TYPE[] must be used as follows:

attachment 1: ATT_NAME[0] + ATT_ORIG_FNAME[0] + ATT_CONTENT[0] + ATT_MIME_TYPE[0]
attachment 2: ATT_NAME[1] + ATT_ORIG_FNAME[1] + ATT_CONTENT[1] + ATT_MIME_TYPE[1]
etc.

Each attachment must add a String to each of the attachment parameters (ATT_NAME[], ATT_ORIGINAL_FILENAME[], ATT_CONTENT[], ATT_MIME_TYPE[]) or else the result will be an ATTACHMENT_PARAMETERS_MISSING error.

If attachments are not used, ATT_NAME[], ATT_ORIGINAL_FILENAME[], ATT_CONTENT[] and ATT_MIME_TYPE[] must all be "null" or must all contain String arrays with length 0. Mixing this will result in an ATTACHMENT_PARAMETERS_MISSING error.

The maximum file size of all attachments combined within the context of a single comment is 2MB (2000 * 1024 bytes). If this constraint is violated a ATTACHMENT_TOO_LARGE will be returned.

Allowed attachment formats are: pdf, jpg and png.

The ADD_COMMENT call can return the following result codes:

Result Code Short Description Description
7001 SUCCESS Comment was successfully added
7002 COMMENT_TEXT_PARAMETER_IS_EMPTY The comment text was not provided
7003 UNAUTHORIZED The user is not allowed to add a comment to this freight document
7004 FREIGHT_DOCUMENT_IS_DRAFT_OR_CANCELLED Comment cannot be added, because the freight document has status 'draft' or 'cancelled'
7005 FREIGHT_DOCUMENT_IS_DELIVERED Comment cannot be added, because the freight document has status 'delivered'
7006 ATTACHMENT_PARAMETERS_MISSING At least one of the attachment parameters is missing
7007 ATTACHMENT_FILE_NOT_READABLE One or more attachment files was not readable
7008 ATTACHMENT_TOO_LARGE The combined size of the attachments exceeds the limit of 2MB

3.12 LAUNCHER_ICON_VISIBILITY call

Integrators which use the integration interface of the TransFollow Android application have the ability to either hide or show the TransFollow application icon in the Android application drawer. This can be used to prevent unintentional interaction with the application directly.

The Intent action name is com.transfollow.tf.intent.action.LAUNCHER_ICON_VISIBILITY and it accepts one parameters:

Extra Name Type Required Default Description
com.transfollow.tf.intent.extras.ICON_VISIBILITY Boolean yes true Whether the icon should be shown or hidden.

When calling this intent, be aware that there is a delay of a couple of seconds before the icon actually hidden or shown.

The LAUNCHER_ICON_VISIBILITY call can return the following result codes:

Result Code Short Description Description
8001 LAUNCHER_ICON_SHOWN The launcher icon of the TF app is now visible
8002 LAUNCHER_ICON_HIDDEN The launcher icon of the TF app is now hidden

3.13 UPDATE_ARRIVAL_DEPARTURE call

With the UPDATE_ARRIVAL_DEPARTURE call the user can update arrival and departure times on the specified freight document if they have a carrier role on it. If the update happens when device is offline the update would be queued and sent to the API when the device goes online.

The Intent action name is com.transfollow.tf.intent.action.UPDATE_ARRIVAL_DEPARTURE and it accepts the following parameters:

Extra Name Type Required Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_ID String yes ID of the freight document for which arrival/departure times should be updated
com.transfollow.tf.intent.extras.COLLECTION_ARRIVAL_TIME String no* The date and time the carrier arrived at the consignor/place of taking over
com.transfollow.tf.intent.extras.COLLECTION_DEPARTURE_TIME String no* The date and time the carrier departed from the consignor/place of taking over
com.transfollow.tf.intent.extras.DELIVERY_ARRIVAL_TIME String no* The date and time the carrier arrived at the consignee/place of delivery
com.transfollow.tf.intent.extras.DELIVERY_DEPARTURE_TIME String no* The date and time the carrier departed from the consignee/place of delivery

*At least one time of arrival/departure has to be provided otherwise the NO_DATE_TIME_PROVIDED result code would be returned.

All dates must be formatted according to ISO 8601 (ISO 8601:2004): YYYY-MM-DDThh:mm:ssZ format. Example: "2014-03-31T22:00:00Z"

The UPDATE_ARRIVAL_DEPARTURE call can return the following result codes:

Result Code Short Description Description
7003 UNAUTHORIZED Returned if account is not carrier on the specified freight document or the freight document does not have the correct status for adjustments.
10001 UPDATE_QUEUED Update was successfully queued.
10002 INVALID_DATE_FORMAT Returned if any of the dates provided has incorrect format.
10003 NO_DATE_TIME_PROVIDED Returned if no arrival/departure times is provided.

3.14 UPDATE_STRUCTURED_GOODS call

With the UPDATE_STRUCTURED_GOODS call it is possible to update goods, RTIs and dangerous goods on a specific freight document.

The Intent action name is com.transfollow.tf.intent.action.UPDATE_STRUCTURED_GOODS and it accepts the following parameters:

Extra Name Required Type Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_ID yes String ID of the freight document for which goods should be updated
com.transfollow.tf.intent.extras.STRUCTURED_GOODS no* ArrayList<Bundle> An ArrayList of Bundle objects type where each object represents goods update
com.transfollow.tf.intent.extras.RTIS no* ArrayList<Bundle> An ArrayList of Bundle objects type where each object represents RTIs update
com.transfollow.tf.intent.extras.DANGEROUS_GOODS no* ArrayList<Bundle> An ArrayList of Bundle objects type where each object represents dangerous goods update

*Either goods, RTIs or dangerous goods to update must be provided

For each goods, RTI or dangerous goods updated you need to provide only the values that you want to update and TransFollow app will handle the rest.

Bundle parameters description for com.transfollow.tf.intent.extras.STRUCTURED_GOODS extra

Parameter Type Required Validations Description
productId java.lang.String no* Must be product id that is present on a freight document Equal to productId returned by server
internalCode java.lang.String no* None Internal identifier of the caller application. This is an external alternative to productId.
numberOfPackages int no Not negative Total number of packages
weight float no None Gross weight per package.
weightUnit java.lang.String no One of the following values: GRAM, KILOGRAM, POUND, METRIC_TON, SHORT_TON, LONG_TON Gross weight unit
netWeight float no None Net weight per package.
netWeightUnit java.lang.String no One of the following values: GRAM, KILOGRAM, POUND, METRIC_TON, SHORT_TON, LONG_TON Net weight unit
volume float no None Volume per package.
volumeUnit java.lang.String no One of the following values: LITRE, CUBIC_METRE, CUBIC_FEET, US_GALLON Volume unit.
commercialValue java.math.BigDecimal no A number from 0 to 9999999999.9999 (besides digits only '.' character is allowed for decimal separation). Commercial value per package.
currency java.lang.String no One of the values according to list ISO4217 (ISO 4217:2008). Currency of the commercial value like: EUR, USD, GBP.

*Either productId or internalCode must be provided

Example:

Bundle goodUpdate = new Bundle();
goodUpdate.putString("productId", "666");
goodUpdate.putInt("numberOfPackages", 123);

goodUpdate.putFloat("weight", 666.55f);
goodUpdate.putString("weightUnit", "KILOGRAM");

goodUpdate.putFloat("volume", 666.33f);
goodUpdate.putString("volumeUnit", "CUBIC_METRE");

goodUpdate.putSerializable("commercialValue", new BigDecimal(88888888888.88888));
goodUpdate.putString("currency", "USD");

Bundle parameters description for com.transfollow.tf.intent.extras.RTIS extra

Parameter Type Required Validations Description
productId java.lang.String no* productId is mandatory Equal to productId returned by server
internalCode java.lang.String no* None Internal identifier of the caller application. This is an external alternative to productId.
numberOfPackages int no Not negative Total number of packages
quality java.lang.String no One of the following values: UNKNOWN, GOOD, MODERATE, NO_FINANCIAL_VALUE RTI quality

*Either productId or internalCode must be provided

Example:

Bundle rtiUpdate = new Bundle();
rtiUpdate.putString("productId", "666");
rtiUpdate.putInt("numberOfPackages", 123);
rtiUpdate.putString("quality", "MODERATE");

Bundle parameters description for com.transfollow.tf.intent.extras.DANGEROUS_GOODS extra

Parameter Type Required Validations Description
productId java.lang.String no* productId is mandatory Equal to productId returned by server
internalCode java.lang.String no* None Internal identifier of the caller application. This is an external alternative to productId.
numberOfPackages int no Not negative Total number of packages
weight float no None Gross weight per package.
weightUnit java.lang.String no One of the following values: GRAM, KILOGRAM, POUND, METRIC_TON, SHORT_TON, LONG_TON Gross weight unit
netWeight float no None Net weight per package.
netWeightUnit java.lang.String no One of the following values: GRAM, KILOGRAM, POUND, METRIC_TON, SHORT_TON, LONG_TON Net weight unit
volume float no None Volume per package.
volumeUnit java.lang.String no One of the following values: LITRE, CUBIC_METRE, CUBIC_FEET, US_GALLON Volume unit.
commercialValue java.math.BigDecimal no A number from 0 to 9999999999.9999 (besides digits only '.' character is allowed for decimal separation). Commercial value per package.
currency java.lang.String no One of the values according to list ISO4217 (ISO 4217:2008). Currency of the commercial value like: EUR, USD, GBP.
technicalName java.lang.String no None. Technical or chemical name.
packingGroup java.lang.String no One of the following values: I, II, III The packing group.
UNNumber java.lang.String no Maximum length of 4 characters. The UN number.
tunnelRestrictionCode java.lang.String no Maximum length of 50 characters. Tunnel restriction code.
labels java.lang.String no None. List of labels.

*Either productId or internalCode must be provided

Example:

Bundle dgoodsUpdate = new Bundle();
dgoodsUpdate.putString("productId", "666");
dgoodsUpdate.putInt("numberOfPackages", 123);
dgoodsUpdate.putString("technicalName", "chemical123");
dgoodsUpdate.putString("UNNumber", "1234");
dgoodsUpdate.putSerializable("labels", "6.1 (3)");
dgoodsUpdate.putSerializable("commercialValue", new BigDecimal(88888888888.88888));
dgoodsUpdate.putString("currency", "USD");

Intent creation example

ArrayList<Bundle> goodUpdates = new ArrayList<>();
goodUpdates.add(goodUpdate);

ArrayList<Bundle> rtiUpdates = new ArrayList<>();
rtiUpdates.add(rtiUpdate);

Intent intent = new Intent("com.transfollow.tf.intent.action.UPDATE_STRUCTURED_GOODS");
intent.putParcelableArrayListExtra("com.transfollow.tf.intent.extras.STRUCTURED_GOODS", goodUpdates);
intent.putParcelableArrayListExtra("com.transfollow.tf.intent.extras.RTIS", rtiUpdates);

The UPDATE_STRUCTURED_GOODS call can return the following result codes:

Result Code Short Description Description
10001 UPDATE_QUEUED Update was successfully queued
11000 STRUCTURED_GOODS_MISSING Goods and RTIs are missing
11001 PRODUCT_ID_MISSING Neither productId nor internalCode was provided for one of the structured goods or RTIs
11002 PRODUCT_NOT_FOUND Product was not found on the specified freight document for provided productId or internalCode
11003 INVALID_WEIGHT_UNIT One of the provided weight units has invalid value
11004 INVALID_VOLUME_UNIT One of the provided volume units has invalid value
11005 INVALID_QUALITY One of the provided quality values is invalid
11006 INVALID_NUMBER_OF_PACKAGES One of the provided number of packages is invalid
11007 INVALID_CURRENCY One of the provided currency values is invalid
11007 INVALID_CURRENCY One of the provided currency values is invalid
11008 GOOD_OR_RTI_IN_WRONG_LIST RTI, GOODS or DANGEROUS GOODS was provided in a list that does not correspond to its type.
11009 INVALID_NET_WEIGHT_UNIT One of the provided net weight units has invalid value
11010 INVALID_PACKING_GROUP One of the provided packing group has invalid value
11011 INVALID_UN_NUMBER One of the UN Number is invalid. Length must be of exactly 4 digits.

3.15 SHOW_RTIS call

With the SHOW_RTIS call it is possible to view RTIs of a specific freight document and update each one of them inside the embedded TransFollow app.

The Intent action name is com.transfollow.tf.intent.action.SHOW_RTIS and it accepts the following parameters:

Extra Name Required Type Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_ID yes String ID of the freight document for which RTIs should be loaded

The SHOW_RTIS call can return the following result codes:

Result Code Short Description Description
2002 FREIGHT_DOCUMENT_NOT_FOUND The freight focument could not be found
15000 RTIS_SHOWN RTIs of the freight document were found and displayed.
15001 RTIS_UPDATED At least one RTI of the freight document was updated in the process

3.16 SHOW_PDF call

The SHOW_PDF call can be used to show the pdf of a freight document. Be aware that if the most recent PDF version cannot be downloaded, such as due to being offline, the user will potentially get to see the PDF of a previous version of the freight document.

The Intent action name is com.transfollow.tf.intent.action.SHOW_PDF and it accepts the following parameter:

Extra Name Type Required Default Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_ID String yes N/A Freight Document ID of the freight document you want to show

The SHOW_PDF call can return the following result codes:

Result Code Short Description Description
12001 PDF_FOUND The PDF was found
12002 PDF_NOT_FOUND The PDF was not available, nor was a version downloadable. This will only be returned if the freight document was found but the PDF was not.
12003 PDF_READER_NOT_FOUND No PDF reader was found on the device to display the PDF

Show the freight documents that need to be in possession of the driver when driving in European countries. By European law the driver is required to show the freight documents which cover transports made with the truck he is currently driving.

The license plate of the truck is used to identify the related freight documents. The license plate can be entered together with the intent or entered by the user in the activity that is started by this intent.

The freight documents will be shown as a list of freight documents. Each document can be opened to see the freight document in more detail.

The Intent action name is com.transfollow.tf.intent.action.SHOW_FREIGHT_DOCUMENTS_FOR_LEGAL_COMPLIANCE and it accepts the following parameter:

Extra Name Type Required Description
com.transfollow.tf.intent.extras.TRUCK_LICENSE_PLATE String no License plate of truck for which to show the documents

The SHOW_FREIGHT_DOCUMENTS_FOR_LEGAL_COMPLIANCE command can return the following result codes:

Result Code Short Description Description
13001 SHOW_FREIGHT_DOCUMENTS_FOR_LEGAL_COMPLIANCE_SUCCESS Successfully finished the intent.

3.18 ENABLE_PUSH_NOTIFICATIONS and DISABLE_PUSH_NOTIFICATIONS

Set whether you want to receive push notification from the TransFollow app.

The corresponding Intent action names are com.transfollow.tf.intent.action.ENABLE_PUSH_NOTIFICATIONS and com.transfollow.tf.intent.action.DISABLE_PUSH_NOTIFICATIONS.

The com.transfollow.tf.intent.action.ENABLE_PUSH_NOTIFICATIONS and com.transfollow.tf.intent.action.DISABLE_PUSH_NOTIFICATIONS command can return the following result codes:

Result Code Short Description Description
16001 UPDATE_PUSH_NOTIFICATION_PREFERENCES_SUCCESS Successfully updated the push notification preferences.

3.19 UPDATE_MILEAGE

With the UPDATE_MILEAGE call the user can update mileage on the specified freight document if they have a carrier role on it. If the update happens when device is offline the update would be queued and sent to the API when the device goes online.

The Intent action name is com.transfollow.tf.intent.action.UPDATE_MILEAGE and it accepts the following parameters:

Extra Name Type Required Validations Description
com.transfollow.tf.intent.extras.FREIGHT_DOCUMENT_ID String yes Must be a valid freight document id which you have the carrier role on ID of the freight document for which mileage should be updated
com.transfollow.tf.intent.extras.VEHICLE_LICENCE_PLATE String no* Must be a valid license plate that belongs to the carrier executing the command. If not provided the mileage would be updated on the freight document level License plate of the vehicle on which the license plate should be updated
com.transfollow.tf.intent.extras.MILEAGE_ARRIVAL float no* At least one value of MILEAGE_ARRIVAL, MILEAGE_DEPARTURE, DISTANCE_TRAVERSED must be provided Mileage on arrival
com.transfollow.tf.intent.extras.MILEAGE_DEPARTURE float no* At least one value of MILEAGE_ARRIVAL, MILEAGE_DEPARTURE, DISTANCE_TRAVERSED must be provided Mileage on departure
com.transfollow.tf.intent.extras.DISTANCE_TRAVERSED float no* At least one value of MILEAGE_ARRIVAL, MILEAGE_DEPARTURE, DISTANCE_TRAVERSED must be provided Total distance traversed
com.transfollow.tf.intent.extras.MILEAGE_UNIT String no One of the following values: KM, MI. Defaults to KM (kilometers) if not provided Unit of measurement

The UPDATE_MILEAGE call can return the following result codes:

Result Code Short Description Description
10001 UPDATE_QUEUED Update was successfully queued.
17000 MILEAGE_NOT_PROVIDED None of the mileage values was provided.
17001 INVALID_MILEAGE_UNIT Unknown mileage unit value was provided.
17002 LICENSE_PLATE_NOT_FOUND The provided vehicle license plate was not found on the role or the user doesn't have access to the provided vehicle.

3.20 SHOW_FREIGHT_DOCUMENT_PDF_QR_CODE

With the SHOW_FREIGHT_DOCUMENT_PDF_QR_CODE code the user can display a QR code which includes the url that contains the latest freight document PDF version.

The Intent action name is com.transfollow.tf.intent.action.SHOW_FREIGHT_DOCUMENT_PDF_QR_CODE and the call can return the following result codes:

Result Code Short Description Description
2002 FREIGHT_DOCUMENT_NOT_FOUND The freight document was not found (or the user does not have access to it)
18000 SHOW_QR_CODE_SUCCESS QR code was successfully shown.
18001 SHOW_QR_CODE_FAILED There was an error showing the QR code.

Miscellaneous result codes

The TransFollow App intents can return the following result codes:

Result Code Short Description Description
1002 INVALID_CREDENTIALS The user is not logged in or the session has expired
9001 ACTION_NOT_PROVIDED An attempt was made to perform an intent call, but the action parameter was not provided
9002 UNKNOWN_CALL Unknown call

The following result codes can also be returned whenever an intent expects a freight document id:

Result Code Short Description Description
2002 FREIGHT_DOCUMENT_NOT_FOUND The freight document was not found (or the user does not have access to it)
2003 FREIGHT_DOCUMENT_ID_PARAMETER_NOT_PROVIDED The required freight document id was not provided

4. Intent Service Usage

A subset of the intents can also be used through the use of the intent service. When these intents are called via the service, no user interface will block the integrator's own application. Also keep in mind that relative to the third party application, the responses returned by the TransFollow app will be returned asynchronously.

These are the differences compared to Activity intent usage as described earlier on this page:

  1. The service has a different className (com.transfollow.tf.routing.TransFollowRoutingService).
  2. It is started with the context.startService method (Pre Android O) or the context.startForegroundService method (Android O and above)
  3. The result of the method is communicated to the third party app asynchronously
  4. To receive the result of a call a Messenger must be implemented by the third party app and passed along in the intent as an Extra
  5. Additional result codes can be returned for errors that would normally be resolved in the TF App's user interface

4.1 Supported Intents

The following intents can also be called via the intent service:

Authentication Calls Viewing Calls (n/a) Freight Document Adjusting Calls Misc Calls
Login Add Comment Poll
Update Arrival/Departure Times Preload
Update Structured Goods

4.2 Intent Implementation

When using the intent service you build up your intent as you would with the activity intents described on this page. You also use a different class name com.transfollow.tf.routing.TransFollowRoutingService and you add an implemented Messenger as additional Extra.

Property Value
App package name (production) com.transfollow.tf
App package name (partner) com.transfollow.tf.partner
Activity class name com.transfollow.tf.routing.TransFollowRoutingService
intent.setClassName("com.transfollow.tf.partner", "com.transfollow.tf.routing.TransFollowRoutingService");
Extra Name Type Required Description
REPLY_TO_MESSENGER Messenger No Your implementation of the Android Messenger class
REQUEST_ID int No A request ID, determined by your implementation to identify to which call a response belongs to
intent.putExtra("REPLY_TO_MESSENGER", callbackMessenger);

4.3 Receiving Results and Full Implementation Example

In order to receive callbacks with result codes in your application you must provide Messenger object as one of the intent extras as described above. If you don't provide one, you will not receive any callback about the call. We highly recommend to always include messenger and handle callback properly.

In addition to the messenger we also highly recommend that you add a requestId. Due to the asynchronous nature of the service the requestId is your only 100% guaranteed way of determining for which of your calls the response applies. When handling the Message in the callback you can use the message.what() function to determine the requestId of the originally sent intent.

Below is a full example of the implementation of a Messenger that will process the responses from the intent service asynchronously and, of course, the implementation of using the intent service.

private static final int LOGIN_REQUEST_ID = 789;

// Messenger object to receive results
private Messenger callbackMessenger = new Messenger(new Handler() {
    @Override
    public void handleMessage(Message msg) {
        // msg.what will contain REQUEST_ID extra value that was provided when creating the intent
        if (msg.what == LOGIN_REQUEST_ID) {
            int responseCode = msg.getData().getInt("responseCode");

            switch(responseCode) {
                case 1001:
                    // Successfully logged in
                    break;
                case 1002:
                    // The user is not logged in
                    break;
                default:
                    // "Something else was wrong
                    break;
            }
        }
    }
});

void performBackgroundLogin(Context context) {
    Intent intent = new Intent();
    intent.setClassName("com.transfollow.tf.partner", "com.transfollow.tf.routing.TransFollowRoutingService");

    boolean isBackgroundServiceAvailable = context.getPackageManager().resolveService(intent, PackageManager.MATCH_DEFAULT_ONLY) != null;
    if (isBackgroundServiceAvailable) {

        intent.setAction("com.transfollow.tf.intent.action.LOGIN");
        intent.putExtra("com.transfollow.tf.intent.extras.USERNAME", "demo@test.tld");
        intent.putExtra("com.transfollow.tf.intent.extras.PASSWORD", "mypassword");

        // unique request identifier, this value will be returned upon receiving the response
        intent.putExtra("REQUEST_ID", LOGIN_REQUEST_ID);

        // You must provide messenger implementation in order to receive responses
        intent.putExtra("REPLY_TO_MESSENGER", callbackMessenger);

        if (Build.VERSION.SDK_INT >= 26) {
            context.startForegroundService(intent);
        } else {
            // Pre-O behavior.
            context.startService(intent);
        }
    } else {
        // Component is not installed, notify the user
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("Error");
        builder.setMessage("Either TransFollow app is not installed or background calls are not available in this version");
        builder.create().show();
    }
}

4.4 Unique Request ID

As you can perform multiple background calls one after another (e.g. adding multiple comments), there is sometimes a need to uniquely match requests to responses. That's why when you provide REQUEST_ID integer extra you will receive the same value in the message as msg.getData().getInt("REQUEST_ID") so you could match you requests with incoming TransFollow responses.

4.5 Additional Result codes

Additional result codes can be returned via the intent service that would not occur via the activity intent. These are the following:

ADD_COMMENT Call

Result Code Short Description Description
9008 EXTERNAL_STORAGE_PERMISSION_ERROR The TF App has not been granted the Android External Storage permission. Please open the TF App permission settings and grant storage permission

5. Recent Changes

The table below highlights the most recent changes to the TF Android integration interface:

Date TF App Version Intent Change
24/04/2018 1.81.3 Show PDF Intent added
24/04/2018 1.81.3 Update Structured Goods internalCode usable for product identification
24/04/2018 1.81.3 Show for Legal Compliance Intent added
24/04/2018 1.86.0 Intent Service Intent Service added
23/05/2018 1.91.0 Sign Multiple Freight Documents Intent added
06/06/2018 1.100.0 Counterparty Signing Intent added
02/07/2018 1.103.0 Show RTIs Intent added
14/08/2018 1.118.1 Update Structured Goods Additional error code 11008 GOOD_OR_RTI_IN_WRONG_LIST added
14/08/2018 1.119.0 Sign / Sign Multiple Freight Documents / Add Comment Resultcodes 3002 and 7004 now also return for cancelled freight documents
18/02/2019 1.389.0 Toggle Push Notifications Intent added
18/02/2019 1.389.0 Update Milage Intent added
18/02/2019 1.???.0 Update Dangerous Goods Update structured goods intent updated