Dispatch SDK
For every Execution Task, a corresponding pre-coded module is expected in the app as part of SDK. This module executes the Task on the device running the Delivery Agent app. Thus, the Dispatcher SDK allows you to build this app with the minimum amount of effort.
The Mobile App SDK will orchestrate the execution of Tasks. Objective status updates will flow back as objective events to dispatch service through API calls made by the SDK and they will be stored in the Objective Events Store. This SDK can be used to render execution task screens on UI, maintain their states, and manage MTS.
Dispatch SDK is an expo-based SDK that is written in Typescript and some Modules in Java/Kotlin. Currently supports SDK>=21 (Android).
Features
- Sync Manager: Provides Sync Manager for syncing events & Docs in the background with retry functionality.
- Execution Tasks: Provides a list of Execution Tasks which are as follows: Deliver, Capture Input, Deliver Cash, Complete-Success, Complete-Failure, Pickup, Doodle, Forms, Display, Verify Location, Verify OTP, Verify String, Scan QR/Barcode, Image Capture, Init Payment, Process Payment, and Complete Payment.
- Execution Engine: Provides methods for Executing a given Workflow/Job in a dispatch.
- Firebase Cloud Messaging: Provides a method for receiving Firebase Cloud Messaging.
- Async Storage: Provides async storage for storing key-value pairs.
- Higher-Order ETs: Provides a way to customize Execution Task UI.
Installation
Setup Expo (For non-expo projects only)
npx install-expo-modules
Install Dispatch SDK Package
npm install @os1-platform/[email protected]_version
yarn add @os1-platform/[email protected]_version
Install these dependencies
{
"dependencies": {
"@apollo/client": "^3.5.6",
"graphql": "^16.2.0",
"@expo-google-fonts/ibm-plex-sans": "*",
"@os1-platform/mts-mobile": "^1.0.0",
"@react-native-async-storage/async-storage": "^1.15.5",
"@react-native-community/datetimepicker": "^3.5.2",
"@react-native-community/netinfo": "^6.0.2",
"@react-native-community/slider": "^4.1.8",
"@react-native-firebase/analytics": "^14.1.0",
"@react-native-firebase/app": "^14.1.0",
"@react-native-firebase/messaging": "^14.1.0",
"@react-native-firebase/remote-config": "^14.1.0",
"@react-navigation/native": "^6.0.6",
"@react-navigation/native-stack": "^6.2.5",
"axios": "^0.24.0",
"expo": "^43.0.4",
"expo-barcode-scanner": "^11.2.1",
"expo-file-system": "~13.0.3",
"expo-font": "~10.0.3",
"expo-image-manipulator": "^10.1.2",
"expo-image-picker": "^11.0.3",
"expo-location": "^13.0.4",
"expo-sqlite": "^10.0.3",
"react": "17.0.2",
"react-native": "0.66.1",
"react-native-paper": "^4.9.2",
"react-native-safe-area-context": "^3.3.2",
"react-native-screens": "^3.9.0",
"react-native-vector-icons": "^9.0.0"
}
}
Async Storage Size Increase
Add the following line in gradle.properties
file for increasing storage (AsyncStorage_db_size_in_MB=10)
android:allowBackup="false"
tools:replace="android:allowBackup"
Add jcenter()
if not added already added in the Project-level build.gradle (/build.gradle): (Android only)
jcenter()
if not added already added in the Project-level build.gradle (/build.gradle): (Android only)buildscript {
repositories {
...
jcenter()
}
}
allprojects {
repositories {
...
jcenter()
...
}
}
Fix for Apollo GraphQL Client (metro.config.js)
Add the following changes in metro.config.js
file. Issue Link
const { getDefaultConfig } = require('metro-config');
const { resolver: defaultResolver } = getDefaultConfig.getDefaultValues();
exports.resolver = {
...defaultResolver,
sourceExts: [...defaultResolver.sourceExts, 'cjs'],
};
Usage
Init Dispatch SDK
import { DispatchSDKManager } from '@os1-platform/dispatch-mobile';
await DispatchSDKManager.getInstance().initDispatchSDK({
userName: 'testuser',
userID: 'testID',
tenantID: 'TENANTID',
tenantBaseURL: 'baseURL',
accessToken: 'accessToken',
});
Init Execution Engine
import { DispatchStateContainer } from '@os1-platform/dispatch-mobile';
/**
* Call this function when dispatch data is fetched successfully
* @param dispatchID
* @param jobs
* @param logging
* @param maxTaskReattempt
*/
await DispatchStateContainer.getInstance().initDispatchExecutor(
dispatchID,
dispatch.data.dispatch.jobs.data as Job[],
false
);
Query Objective Status
import { DispatchStateContainer } from '@os1-platform/dispatch-mobile';
/**
*
* @param objRef
* @param jobID
*/
DispatchStateContainer.getInstance().fetchObjectiveState(
'objective_ref',
'job_id'
);
Start Objective Execution
interface sdkError {
code: string;
message: string;
}
// Error Handling from the calling Screen
React.useEffect(() => {
if (route.params?.sdkError) {
console.log(JSON.stringify(route.params.sdkError));
Alert.alert('Error', JSON.stringify(route.params.sdkError));
}
}, [route.params?.sdkError]);
navigation.navigate('DispatchExec', {
success: true,
objectives: [], // Pass objective List
successRoute: 'SuccessScreen',
failureRoute: 'FailureScreen',
initRoute: 'TaskDetail',
// Calling Screen name (optional parameter). Used to throw errors back to calling screen
});
FCM
Setup (Android only)
Add the Firebase Android configuration file to your app:
- Create a firebase project (Check Firebase instructions for creating an app).
- Click Download google-services.json to obtain your Firebase Android config file (google-services.json).
- Move your config file into the module (app-level) directory of your app.
- To enable Firebase products in your app, add the google-services plugin to your Gradle files.
In your module (app-level) Gradle file (usually app/build.gradle), apply the Google Services Gradle plugin:
apply plugin: 'com.android.application'
// Add the following line:
apply plugin: 'com.google.gms.google-services' // Google Services plugin
android {
// ...
}
In your root-level (project-level) Gradle file (build.gradle), add rules to include the Google Services Gradle plugin. Check that you have Google's Maven repository, as well.
buildscript {
repositories {
// Check that you have the following line (if not, add it):
google() // Google's Maven repository
}
dependencies {
// ...
// Add the following line:
classpath 'com.google.gms:google-services:4.3.10' // Google Services plugin
}
}
allprojects {
// ...
repositories {
// Check that you have the following line (if not, add it):
google() // Google's Maven repository
// ...
}
}
Get FCM Token
import {
getFCMToken,
requestFirebasePermissions,
} from '@os1-platform/dispatch-mobile';
//Get FCM Token
let token = await getFCMToken();
//Request Notifications permissions (ios)
// It will ask for user permissions for showing alert/notifications
let enabled = await requestFirebasePermissions();
Background FCM Messages
Register callback for receiving FCM Messages in the background.
NOTE
Call this function in the root component, i.e, in the
index.js
file of the app.
import { registerBackgroundHandler } from '@os1-platform/dispatch-mobile';
registerBackgroundHandler((message: object) => {
// Handle FCM Message here
});
Foreground FCM Messages
useFcmMessage() custom hook in functional components to receive FCM Messages in Foregound.
import { useFCMMessage } from '@os1-platform/dispatch-mobile';
const fcmMessage = useFCMMessage();
if (fcmMessage != null) {
// update UI here to show the message
}
OR
Extend FCM base class in case of class components
import { FCM } from '@os1-platform/dispatch-mobile';
class ClassComponent extends FCM {
//implement these methods
handleFcmMessage(remoteMessage: object): void {}
//implement these methods
handleNotification(remoteMessage: object): void {}
}
MTS
In the case of the dispatch mobile app, the Location Tracking module of the SDK provides a configurable way to capture and stream location data from the mobile device to the MTS service, without any user intervention. To learn more, see Dispatch Documentation.
MTS Default Config
export class MTSDefaults {
locationFrequency: number = 10000; // in milli seconds (no. of seconds after which location updates will happen)
distanceAccuracyLimit: number = 250; // in metres
speedLimit: number = 28; // in m/s
mode: MTSMode = MTSMode.HYBRID;
environment: MTSEnv = MTSEnv.DEV;
batchSize: number = 25;
isMqttCleanSession: boolean = true;
mqttKeepAliveInterval: number = 15 * 60; //in seconds
maxLocationAge: number = 15000; // in milliseconds
maxTraceSession: number = 24 * 3600 * 100; //in milliseconds
isOdometerEnabled: boolean = true;
retriesBeforeFallback: number = 1;
httpFailureLimit: number = 5;
dataSendDelay: number = 30000; // in milli seconds
alarmTime: number = 60000; // in milli seconds
missingSeqCheckDuration: number = 5 * 60 * 1000; // in milli seconds
odometerPushFrequency: number = 5 * 60 * 1000; // in milli seconds
qosLevel: number = 1; // values can be 0 ,1
}
Check For Mandatory MTS Permissions
let granted = await MtsLib.requestPermissionsForMTS();
// if granted = true : all permissions granted
// granted = false : one or more permissions denied
Init MTS
import type { MTSInitRequest } from '@os1-platform/mts-mobile';
let mtsDefaults = new MtsLib.MTSDefaults();
mtsDefaults.speedLimit = 5000;
mtsDefaults.locationFrequency = 10000;
mtsDefaults.environment = MtsLib.MTSEnv.PRE_PROD;
mtsDefaults.isOdometerEnabled = false;
//Change MTS default values as per your use case
// ...Use this for Initiating MTS
let mtsInitReq: MTSInitRequest = {
appName: 'app_name',
appVersion: '1',
mtsDeviceID: 'deviceId', // use FCM ID here
configData: mtsDefaults,
baseURL: 'https://{tenantId}.example.io/{mtsEndpoint}',
accessToken: 'token',
tenantID: 'TENANTID',
};
await MtsLib.initMTS(mtsInitReq);
//See Error Codes for Possible error types
Start MTS
let startReq: MTSStartRequest = {
accessToken: 'token', // update access token
resetSequence: false,
dispatchID: '12345', // pass dispatch ID here
expiryTime: Date.now() + 24 * 2600 * 1000, // expiry time after which MTS will stop automatically
};
await MtsLib.startMTS(startReq);
Publish Event
await MtsLib.publishEvent('TESTEVENT', { battery: 56, network: 100 });
Stop MTS
await MtsLib.stopMTS();
Error Codes
PERMISSIONS_ERROR[2500] = 'Mandatory Android Permissions not provided';
MTS_INIT_ERROR[2501] = 'MTS INIT Not called! MTS Device ID is Empty';
PARAM_MISSING[2502] = 'Mandatory Paramater is missing in request';
Sync Manager
Import Sync Manager
import { AppSyncManager, SdkSyncType } from '@os1-platform/dispatch-mobile';
// Start Events sync
await AppSyncManager.getInstance().startSyncing(
false, // pass true for force sync
SdkSyncType.EVENTS_SYNC
);
//Start Documents sync
await AppSyncManager.getInstance().startSyncing(
false, // pass true for force sync
SdkSyncType.DOCUMENT_SYNC
);
//Get All Events By Dispatch ID
await AppSyncManager.getInstance().getAllEvents('dsp_id');
//Get all documents By Dispatch ID
await AppSyncManager.getInstance().getAllDocuments('dsp_id');
Start Sync Manager as a Foreground Service in android
import { NativeSyncManager } from '@os1-platform/dispatch-mobile';
NativeSyncManager.startSyncManager(
interval,
notificationTitle,
notificationText
);
// interval will be in seconds
NativeSyncManager.startSyncManager(
2000,
'Dispatch Service',
'Syncing events...'
);
//To stop the foreground android service
NativeSyncManager.stopSyncManager();
SDK Utility Methods
import { SdkUtils } from '@os1-platform/dispatch-mobile';
await SdkUtils.getRemoteConfig(3000);
// 3000 is the number of seconds to cache the config
Download API from Public URL
/**
* Function to download apk file from a public URL
* @param apkURL - URL where apk is hosted
* @param version - expected version of apk (used for naming the file)
* @param callback - callback for getting progress of download
*/
await SdkUtils.downloadAPK('https://apk_url.com', '1', (progress) =>
console.log(progress.totalBytesWritten)
);
Open and Install an APK file
/**
* Opens & Install an APK file
* @param uri - source of apk file
*/
await SdkUtils.openAPKFile(result.uri);
Send events to Firebase analytics
/**
*
* @param eventName-> string
* @param tag -> string
* @param message -> string
*/
await Logger.getInstance().sendToFirebaseAnalytics('ev_name', 'tag', 'message');
Log events to console
/**
*
* @param TAG
* @param message
* @param logType
*/
Logger.getInstance().logEvent('tag', 'message', LOG_TYPE.SDK_ERROR);
Error Codes
const enum BaseErrorCodes {
InvalidArgumentError = '100100',
InvalidBaseURL = '100101',
SyncManagerNotInitialized = '100102',
MissingOrInvalidProps = '100103',
SQLiteDBIssue = '100104',
AppSyncNotInitialized = '100105',
FMS_FOLDER_CREATION_ERROR = '100106',
REASON_CODE_API_ERROR = '100107',
MERGING_ERROR = '100108',
LOCATION_PERMISSION_DENIED = '100109',
CAMERA_PERMISSION_DENIED = '100110',
STORAGE_PERMISSION_DENIED = '100111',
GRAPHQL_CLIENT_NOT_INITIALIZED = '100112',
FMS_GRAPHQL_API_ERROR = '100113',
INTERNET_NOT_ENABLED = '100114',
LOCATION_OR_GPS_NOT_ENABLED = '100115',
EXECUTION_ENGINE_ERROR = '100116',
UNEXPECTED_ERROR = '100117',
}
Updated 6 months ago