SAP’s Mobile Offline capability to improve operational efficiency and staff productivity

These days the smartphone usage has increased adversely and it is expected to grow in the coming years.

Several business process has transformed mobile and is changing people’s life throughout the world. Mobile applications are handy to use and allows us to connect from anywhere and everywhere. Most of the applications are internet driven which causes a limitation of usage.

In order to break this boundary and to provide business continuity we can now develop offline mobile applications. You will need internet only during the start and end of the business process. Rest all can function offline. But there is no limitation to fetch real time data while having good internet connectivity.

In this blog, I would like to share my experience in designing an offline application and the lessons learnt during this journey.

SAP provides various offerings to enable this feature. With SAP BTP in existence and the SAP Mobile Service to consume, below were the list of possibilities we considered.

  • SAP Hybrid Application Toolkit
  • SAP Mobile Development Kit
  • SAP BTP SDK for native mobile application development

SAP these days doesn’t recommend their customers to implement new applications with SAP Hybrid Application Toolkit (HAT). Hence, we went for alternatives.

The SAP Mobile Development Kit (MDK) is a tool to build hybrid applications(mobile and web). The code will be written once in SAP BAS and can be rendered on multiple devices as native mobile application on iOS and Android and as web application in a browser. MDK provides a wide range of UI styling. MDK documentation can be referred for the same. As our customer is brand focused, we observed deviations from the possible styling opportunities provided by MDK, so we opted for SAP BTP SDK for Android.

To better understand the approach of the online/offline scenario, I pasted the screenshot of the process flow diagram :-

It is a scenario of Delivery PGI in warehouse management process where the entire open shipment and delivery details for a plant gets downloaded onto the mobile device with which the user can transact. Referred to as initial or full sync. The offline updated data is posted to ECC. SAP Mobile Service delta sync kicks in, to sync the local store with modified records from ECC.

Delivery%20PGI%20Process%20Flow

Delivery PGI Process Flow

Delivery%20PGI%20Flow%20continued...

Delivery PGI Flow continued…

While designing the offline application, I recommend to segregate the master data APIs and the transactional APIs. The reason being, we call the master data API during the initial login for reusability purposes in comparison to the transactional data which is called when the respective application gets triggered.

Another point to consider, is the size of data that the application is expected to download. It’s a good practice to bring only that fragment of data which you expect to work with. Adding date filters to the API is the best way to target these records and will improve performance.

SAP framework doesn’t allow the utilization of the navigation property of the oData service with offline delta sync functionality. Hence, we split the oData call into multiple APIs and created our local store.

The response format of an oData delta sync call is ATOM+XML whereas the standard SAP oData response call is JSON format. SAP Mobile Service Offline configuration needs alteration to support both.

Value added services to consider:

  • A feature to notify the user, when unsynched offline record gets unnoticed, while the user logs off from the application.
  • Download feature of erroneous records (Error Archives) helps user track the failed transactions and take appropriate action either via mobile or backend ECC.

Below are few code snippets which I would like to share.

Android Implementation

Step 1: Add SAP BTP Dependencies

implementation group:'com.sap.cloud.android', name:'odata', version: sdkVersion
implementation group: 'com.sap.cloud.android', name: 'offline-odata', version: sdkVersion

Step 2: Initialize SAP Offline Store

The step below refers to the communication to base URL and creation of offline store.

val offlineDELPGIDataProvider = OfflineODataProvider( URL("$serviceUrl$GW_OFS_Dest/Z_OFS_DELIVERY_PGI_OFFLINE_SRV_01"), offlineODataParameters, getClient(), delegate
)

OffineODataProvider is a 4 parameter class. The object created from this class is used to upload, download, clear the offline database.

Param 1: URL

The first parameter is the base url of backend server from where we download data.

Ex: URL(“$serviceUrl$GW_OFS_Dest/Z_OFS_DELIVERY_PGI_OFFLINE_SRV_01”),

Param 2: Database configuration

The second parameter is OfflineODataParameter object in which we define the name of offline store and encryption type.

val offlineODataParameters = OfflineODataParameters().apply { isEnableRepeatableRequests = false storeName = OFFLINE_DEL_PGI_DATASTORE currentUser = FlowContextRegistry.flowContext.getCurrentUserId() isForceUploadOnUserSwitch = runtimeMultipleUserMode isEnableIndividualErrorArchiveDeletion = true val encryptionKey = if (runtimeMultipleUserMode) { UserSecureStoreDelegate.getInstance().getOfflineEncryptionKey() } else { //If is single user mode, create and save a key into user secure store for accessing offline DB if (UserSecureStoreDelegate.getInstance().getData<String>( OFFLINE_DATASTORE_ENCRYPTION_KEY ) == null ) { val bytes = ByteArray(32) val random = SecureRandom() random.nextBytes(bytes) val key = Base64.encodeToString(bytes, Base64.NO_WRAP) UserSecureStoreDelegate.getInstance().saveData( OFFLINE_DATASTORE_ENCRYPTION_KEY, key ) Arrays.fill(bytes, 0.toByte()) key } else { UserSecureStoreDelegate.getInstance().getData<String>( OFFLINE_DATASTORE_ENCRYPTION_KEY ) } } Log.d("ResponseEncryption", encryptionKey.toString()) storeEncryptionKey = encryptionKey
}

Param 3: OkHttp Client

The third parameter is okHttp object in which we define the read and write timeouts.

private fun getClient(): OkHttpClient { return ClientProvider.get().newBuilder().connectTimeout(300, TimeUnit.SECONDS) .readTimeout(300, TimeUnit.SECONDS).writeTimeout(300, TimeUnit.SECONDS).build()
}

Param 4: OfflineODataDelegate

The methods of this delegate are called at various stages during the execution of open, download, and upload.

Step 3: Define the Defining Query (EntitySets)

So far we created an object OfflineODataProvider and now we include the names of entities we will be supporting offline. SAP SDK will download the data of all referenced APIs from server and stores it into local store created.

offlineDELPGIDataProvider = OfflineODataProvider( URL("$serviceUrl$GW_OFS_Dest/Z_OFS_DELIVERY_PGI_OFFLINE_SRV_01"), offlineODataParameters, getClient(), delegate
).apply { val deliveryQuery = DataQuery() deliveryQuery.filter(GetDeliveryDetails.plant.equal(localSharedStorage?.getPlant()!!)) Log.d("Responded", deliveryQuery.toString()) addDefiningQuery(OfflineODataDefiningQuery("GetDeliveriesForShipmentSet", "GetDeliveriesForShipmentSet$deliveryQuery", false)) addDefiningQuery(OfflineODataDefiningQuery("GetDeliveryDetailsSet", "GetDeliveryDetailsSet$deliveryQuery", false)) addDefiningQuery(OfflineODataDefiningQuery("GetDeliveryItemDetailsSet", "GetDeliveryItemDetailsSet$deliveryQuery", false)) z_DEL_PGI_OFFLINE_SRV_Entities_Offline = Z_DELIVERY_PGI_SRV_Entities(this)
}

We need to add OfflineODataDefiningQuery object to provider. OfflineODataDefiningQuery object creation requires three parameters.

Param 1: End point method

Method name of Entity which act as end point.

Param 2: Query Param

We need to pass data query to access the data from mentioned end point.

Ex: val deliveryQuery = DataQuery()
deliveryQuery.filter(GetDeliveryDetails.plant.equal(localSharedStorage?.getPlant()!!))

Param 3: Automatically Retrieves Streams

Boolean value which indicates if the query needs to retrieve streams automatically.

Step 4: SAP Store actions

Three actions allowed.

1.Open: Prior to performing tasks on offline store, we need to make sure its opened and available.

2.Download: Once offline store is open we can download data from server based on defining query using download method in provider object.

Ex: provider.download()

The download method refers the defining queues and download data from server. The downloaded data is stored to local store and returns either success or failure call back method.

provider.download({ countDownLatch.countDown()
}, { errorMessage = it.message ?: "Unknown offline sync error when downloading data." result = getErrorCode(it) countDownLatch.countDown()
})

3.Upload: When the modified content awaits in request queue to sync with server, we will call the upload method from provider class.

Ex: provider.upload()

This upload method internally uploads all the data from request Queue and return success or failure call back method.

provider.upload({ startPointForSync = progressListener.totalStepsForTwoProgresses / 2 }, { countDownLatch.countDown() errorMessage = it.message ?: "Unknown offline sync error when uploading data."
})

Step 5: Accessing Data from Offline Store

On successful download, we use service class generated from metadata to access the methods or end points.

val data = z_delivery_pgi_srv_entities_offline?.getGetDeliveriesForShipmentSet(query) if (data != null && data.isNotEmpty()) { Log.d("Response", "Size " + data.size) view?.onGetShipments(data) view?.onDismissLoader() } else { view?.onDismissLoader() view?.onError(context.resources.getString(R.string.no_data_found))
}

Hopefully, you found this blogging helpful, and I wish it helps you avoid some of the common mistakes while designing an offline mobile application. Appreciate your feedback, comments.

Follow me as I will be posting few more blogs on topics related to SAP Mobile Platform and SAP Mobile Services.

References:

https://developers.sap.com/tutorials/cp-sdk-android-wizard-app-offline.html

https://help.sap.com/doc/f53c64b93e5140918d676b927a3cd65b/Cloud/en-US/docs-en/guides/getting-started/android/setup.html

https://blogs.sap.com/2013/09/30/how-to-implement-basic-delta-query-support-in-sap-netweaver-gateway/

You can also find similar blogposts on various topics on SAP BTP SDK for Android in the link mentioned below:

https://blogs.sap.com/tags/73555000100800001281/