Skip to content

Progress API

Provides details about the progress of various operations, such as initial open, upload, download, and send store.

The enhanced Progress API provides greater details about the progress of various operations, such as initial open, upload, download, and send store. The progress is measured in steps. The information includes: the total number of steps in the operation, the current step number, the name of the step, the time of the last update, a parameter array for the current step, and a default message.

Default messages have been translated into these supported languages. Based on the language of the device, default messages of the corresponding language are displayed as part of the progress information.

The step information is updated either by the mobile client or by the server depending on where the step is running.

Remarks

  • An application may have multiple defining queries in an initial open or download operation. The processing of all defining queries is treated as a single step, while the defining query name and URL will be updated in the parameter array.

  • An application may have multiple requests to send to the server in an upload operation. The processing of all requests is treated as a single step, while the request number is updated in the parameter array of this step.

  • A batch request may contain multiple requests. When processing a batch request, the server provides an individual description for each request.

  • The cloudProgressPullInterval parameter controls how often status is pulled from the server. The default interval is 2000 ms.

    • To get more frequent progress updates, developers can adjust the cloudProgressPullInterval to be a shorter interval.

    • To avoid the server being overloaded, the minimum pulling interval is 500 ms.

  • The total number of steps is the total number of possible steps that can occur for the operation.

    • Steps can be skipped. The total number of steps includes conditional steps. But if the condition is not met, these steps will be skipped.

    • Total number of steps can increase during runtime. When a metadata change or delta link expiration is detected, the total number of steps will increase to include the additional steps required.

Table of Steps

The following table lists the steps currently supported by Offline OData, along with the associated default messages in English.

Step Message Variable Parameters
buildingEntityStore Performing step <currentStepNumber> of <totalNumberOfSteps>: Building the entity store  
creatingBootStrapDatabase Performing step <currentStepNumber> of <totalNumberOfSteps>: Creating bootstrap database  
creatingRequestQueueDatabase Performing step <currentStepNumber> of <totalNumberOfSteps>: Creating request queue database  
downloadingEntityStore Performing step <currentStepNumber> of <totalNumberOfSteps>: Downloading entity store. Received <Parameters[0]> of <Parameters[1]> bytes Parameters[0]: received bytes Parameters[1]: total bytes
erasingExpiredRequests Performing step <currentStepNumber> of <totalNumberOfSteps>: Erasing expired requests  
loadingMetadata Performing step <currentStepNumber> of <totalNumberOfSteps>: Performing request queue optimizer  
performingTransactionMerge Performing step <currentStepNumber> of <totalNumberOfSteps>: Performing transaction builder  
performingCreateDeleteMerge Performing step <currentStepNumber> of <totalNumberOfSteps>: Performing undo local creation optimizer  
processingDefiningQueries Performing step <currentStepNumber> of <totalNumberOfSteps>: Processing defining queries <Parameters[0]> using <Parameters[1]> Parameters[0]: request name Parameters[1]: request url
processingRequests Performing step <currentStepNumber> of <totalNumberOfSteps>: Processing requests. Processing modification request <Parameters[0]> of <Parameters[1]> Parameters[0]: request number Parameters[1]: total request number
reapplyingChanges Performing step <currentStepNumber> of <totalNumberOfSteps>: Reapplying changes  
analyzingReceivedData Performing step <currentStepNumber> of <totalNumberOfSteps>: Receiving data from the client  
receivingDataFromServer Performing step <currentStepNumber> of <totalNumberOfSteps>: Receiving data from the server. Received <Parameters[0]> bytes Parameters[0]: received bytes
removingDeletedRelationships Performing step <currentStepNumber> of <totalNumberOfSteps>: Removing deleted relationships  
removingRemoveAfterUploadRequests Performing step <currentStepNumber> of <totalNumberOfSteps>: Removing RemoveAfterUpload requests  
sendingDataToServer Performing step <currentStepNumber> of <totalNumberOfSteps>: Sending data to the server. Sent <Parameters[0]> bytes Parameters[0]: sent bytes
sendingEntityStoreDatabase Performing step <currentStepNumber> of <totalNumberOfSteps>: Sending entity store database.  
sendingRequestQueueDatabase Performing step <currentStepNumber> of <totalNumberOfSteps>: Sending request queue database.  
uploadPreprocessing Performing step <currentStepNumber> of <totalNumberOfSteps>: Pre-processing requests before uploading  
waitingForDownload Performing step <currentStepNumber> of <totalNumberOfSteps>: Waiting for download  

For more information about OfflineODataProviderDelegate, see OfflineODataProviderDelegate, below.

Enhanced Progress API Example

Prepare to initialize an OfflineODataProvider.

// Note: you need to replace variable values below starting with "YOUR" with your own values.
String storeName = "YOUR_STORE_NAME";
String host = "YOUR_SERVER_HOST";
int port = "YOUR_PORT";
String serviceName = "YOUR_SERVICE_NAME";
String url = ` "https://" + host + ":" + port + "/" + serviceName + "/" `;
URL myServiceRoot = new URL(url);

// Set up an instance of OfflineODataParameters. See OfflineODataParameters class document for details.
OfflineODataParameters storeParams = new OfflineODataParameters();
storeParams.setStoreName(storeName);
// Note: you need to replace variable values below starting with "YOUR" with your own values.
val storeName = "YOUR_STORE_NAME"
val host = "YOUR_SERVER_HOST"
val port = "YOUR_PORT"
val serviceName = "YOUR_SERVICE_NAME"
val url = ` "https://${host}:${port}/${serviceName}/" `
val myServiceRoot = URL(url)

// Set up an instance of OfflineODataParameters. See OfflineODataParameters class document for details.
val storeParams = OfflineODataParameters()
storeParams.storeName = storeName
/// Note: you need to replace the variable values below starting with "YOUR" with your own values.
let storeName = "YOUR_STORE_NAME"
let host = "YOUR_SERVER_HOST"
let port = "YOUR_PORT"
let serviceName = "YOUR_SERVICE_NAME"
let myServiceRoot = URL(string: "https://(host):(port)/(serviceName)/")!

/// Set up an instance of OfflineODataParameters. See the OfflineODataParameters class document for details.
var storeParams = OfflineODataParameters()
storeParams.storeName = storeName

/// CPmsURLSessionDelegate is your implemetation of SAPURLSessionDelegate for handling authentication or other tasks.
let cpmsURLSessionDelegate = CPmsURLSessionDelegate()

/// Set up an instance of SAPURLSession for authentication. See the SAPURLSession class document for more details.
let mySAPURLSession = SAPURLSession(configuration: URLSessionConfiguration.default, delegate: cpmsURLSessionDelegate)
    mySAPURLSession.register(SAPcpmsObserver(applicationID: "YOUR_APP_ID"))

Create a class adopting the OfflineODataProviderDelegate protocol to enable the callback mechanism.

public class OfflineODataProviderDelegateSample implements OfflineODataProviderDelegate {
    ...
class OfflineODataProviderDelegateSample: OfflineODataProviderDelegate {
    ...
public class OfflineODataProviderDelegateSample: OfflineODataProviderDelegate {
    ...

Implement app logic relating to progress status inside these inherited functions for different offline operations, such as open, upload, download, and send store.

@Override
public void updateOpenProgress(@NonNull OfflineODataProvider provider, @NonNull OfflineODataProviderOperationProgress progress) {
    /*
    updateYourOwnProgressBar(progress)

    In this instance, “progress” is the structure that contains all the step information, such as step number and default message. Here, you can apply logic against this progress information.
    For example, you can implement the UpdateYourOwnProgressBar function to update the progress bar by continuously adjusting the percent complete (current step number / total number of steps). You can also implement a function to update the status with information about what is being processed in the current step.
    */
}

@Override
public void updateDownloadProgress(@NonNull OfflineODataProvider provider, @NonNull OfflineODataProviderDownloadProgress progress) {
    // Include progress status update for Download
    // updateYourOwnProgressBar(progress)
}

@Override
public void updateUploadProgress(@NonNull OfflineODataProvider provider, @NonNull OfflineODataProviderOperationProgress progress) {
    // Include progress status update for Upload
    // updateYourOwnProgressBar(progress)
}

@Override
public void updateFailedRequest(@NonNull OfflineODataProvider provider, @NonNull OfflineODataFailedRequest request) {
    // Include progress status update for Failed Requests
    // updateYourOwnProgressBar(request)
}

@Override
public void updateSendStoreProgress(@NonNull OfflineODataProvider provider, @NonNull OfflineODataProviderOperationProgress progress) {
    // Include progress status update for Send Store
    // updateYourOwnProgressBar(progress)
}
override fun updateOpenProgress(provider: OfflineODataProvider, progress: OfflineODataProviderOperationProgress) {
    /*
    updateYourOwnProgressBar(progress)

    In this instance, “progress” is the structure that contains all the step information, such as step number and default message. Here, you can apply logic against this progress information.
    For example, you can implement the UpdateYourOwnProgressBar function to update the progress bar by continuously adjusting the percent complete (current step number / total number of steps). You can also implement a function to update the status with information about what is being processed in the current step.
    */
}

override fun updateDownloadProgress(provider: OfflineODataProvider, progress: OfflineODataProviderDownloadProgress) {
    // Include progress status update for Download
    // updateYourOwnProgressBar(progress)
}

override fun updateUploadProgress(provider: OfflineODataProvider, progress: OfflineODataProviderOperationProgress) {
    // Include progress status update for Upload
    // updateYourOwnProgressBar(progress)
}

override fun updateFailedRequest(provider: OfflineODataProvider, request: OfflineODataFailedRequest) {
    // Include progress status update for Failed Requests
    // updateYourOwnProgressBar(request)
}

override fun updateSendStoreProgress(provider: OfflineODataProvider, progress: OfflineODataProviderOperationProgress) {
    // Include progress status update for Send Store
    // updateYourOwnProgressBar(progress)
}
public func offlineODataProvider(provider: OfflineODataProvider, didUpdateOpenProgress progress: OfflineODataOperationProgress) -> Void {
    /*
    UpdateYourOwnProgressBar(progress)

    In this instance, “progress” is the structure that contains all the step information, such as step number and default message. Here you can apply logic against this progress information.
    For example, you can implement the `UpdateYourOwnProgressBar` function to update the progress bar by continuously adjusting the percent complete (current step number / total number of steps). You can also implement a function to update the status with information about what is being processed in the current step.
    */
}

public func offlineODataProvider(provider: OfflineODataProvider, didUpdateDownloadProgress progress: OfflineODataDownloadProgress) -> Void {
    // Include progress status update for Download
    // UpdateYourOwnProgressBar(progress)
}

public func offlineODataProvider(provider: OfflineODataProvider, didUpdateUploadProgress progress: OfflineODataOperationProgress) -> Void {
    // Include progress status update for Upload
    // UpdateYourOwnProgressBar(progress)
}

public func offlineODataProvider(provider: OfflineODataProvider, requestDidFail request: OfflineODataFailedRequest) -> Void {
    // Include progress status update for Failed Requests
    // UpdateYourOwnProgressBar(request)
}

public func offlineODataProvider(provider: OfflineODataProvider, didUpdateSendStoreProgress progress: OfflineODataOperationProgress) -> Void {
    // Include progress status update for Send Store
    // UpdateYourOwnProgressBar(progress)
}

Initialize an OfflineODataProvider instance with a delegate, and create a DataService instance.

// Set up a delegate instance.
OfflineODataProviderDelegateSample myDelegate = new OfflineODataProviderDelegateSample();
OfflineODataProvider provider = new OfflineODataProvider(serviceRoot, parameters, httpClient, myDelegate);
DataService service = new DataService(provider);
// Set up a delegate instance.
val myDelegate = OfflineODataProviderDelegateSample()
val provider = OfflineODataProvider(serviceRoot, parameters, httpClient, myDelegate)
val service = DataService(provider)
/// Set up a delegate instance.
let myDelegate = OfflineODataProviderDelegateSample()
let provider = try OfflineODataProvider(serviceRoot: myServiceRoot, parameters: storeParams, sapURLSession: mySAPURLSession, delegate: myDelegate)
let service = DataService(provider: provider)

Set the frequency for the app to fetch status from the server by changing the default value of cloudProgressPullInterval and fine-tune as needed. In this example, we change it from its default value 2000 ms to 600 ms.

@Override
public int getCloudProgressPullInterval() {
    return 600;
    // The default cloud progress status pull interval is 2000ms. Here you can set it to a time period of your choice.
}
override fun getCloudProgressPullInterval(): Int {
    return 600
    // The default cloud progress status pull interval is 2000ms. Here you can set it to a time period of your choice.
}
public var cloudProgressPullInterval: Int {
    get {
        return 600;
        // The default cloud progress status pull interval is 2000ms. Here you can set it to a time period of your choice.
    }
}

The app performs its own work. In this example, the app performs an initial open.

// Add defining query for Customers, Orders, OrderItems and Products
OfflineODataDefiningQuery customerDef = new OfflineODataDefiningQuery("Customers", "Customers", false);
this.provider.addDefiningQuery(customerDef);
this.provider.addDefiningQuery(new OfflineODataDefiningQuery("Orders", "Orders", false));
this.provider.addDefiningQuery(new OfflineODataDefiningQuery("OrderItems", "OrderItems", false));
this.provider.addDefiningQuery(new OfflineODataDefiningQuery("Products", "Products", false));
this.provider.open(successHandler, failureHandler);
// Add defining query for Customers, Orders, OrderItems and Products
val customerDef = OfflineODataDefiningQuery("Customers", "Customers", false)
this.provider.addDefiningQuery(customerDef)
this.provider.addDefiningQuery(OfflineODataDefiningQuery("Orders", "Orders", false))
this.provider.addDefiningQuery(OfflineODataDefiningQuery("OrderItems", "OrderItems", false))
this.provider.addDefiningQuery(OfflineODataDefiningQuery("Products", "Products", false))
this.provider.open(successHandler, failureHandler)
let customerDQ = OfflineODataDefiningQuery(name: "Customers", query:"Customers", automaticallyRetrievesStreams: false)
// Add defining query for Customers
try provider.add(definingQuery: customerDQ)

// Add defining query for Orders
try provider.add(definingQuery: OfflineODataDefiningQuery(name: "Orders", query:"Orders", automaticallyRetrievesStreams: false))

// Trigger initial open. During execution, the callback function will be called and the app logic is executed.
try provider.open()

// In this example, progress information will be appended into array “updates”. You can implement a function here to process progress information. For the structure and default values of the progress status information, please see Delegate.

In this example, progress information will be appended into the "updates" array. You can implement your own function here to process progress information. For instance, use progress information to update a progress bar, then you can see the progress bar moving forward during the initial open:

private void updateYourOwnProgressBar(@NonNull OfflineODataProviderOperationProgress progress) {
    ProgressBar progressBar = findViewById(R.id.progressBar);
    progressBar.setIndeterminate(false);
    progressBar.setMax(progress.getTotalNumberOfSteps());
    progressBar.setProgress(progress.getCurrentStepNumber());
}
private fun updateYourOwnProgressBar(progress: OfflineODataProviderOperationProgress) {
    val progressBar = findViewById(R.id.progressBar)
    progressBar.isIndeterminate = false
    progressBar.max = progress.totalNumberOfSteps
    progressBar.progress = progress.currentStepNumber
}

Note

You must make sure the updating of progress bar is in a UI thread or a coroutine scope.

For the structure and default values of the progress status information, see OfflineODataProviderDelegate, below.

OfflineODataProviderDelegate

Delegates are used to get progress updates while an OfflineODataProvider is opening, downloading, downloading files, uploading, sending the store to the server, and gathering information about requests that fail during an upload. The public protocol is OfflineODataProviderDelegate.

Application developers can adopt from the OfflineODataProviderDelegate public protocol, and then make extensions to the predefined functions of each progress phase.

For more details please refer to the OfflineODataProviderDelegate section of the online help.

Supported Languages

Default messages are available in these languages:

  • Arabic (ar)
  • Bosnian (bs)
  • Bulgarian (bg)
  • Croatian (hr)
  • Czech (cs)
  • Danish (da)
  • Dutch (nl)
  • English (en)
  • French (fr)
  • German (de)
  • Greek (el)
  • Hebrew (he)
  • Hungarian (hu)
  • Italian (it)
  • Japanese (ja)
  • Korean (ko)
  • Malay (ms)
  • Norwegian (no)
  • Polish (pl)
  • Portuguese (pt)
  • Romanian (ro)
  • Russian (ru)
  • Simplified Chinese (zh-Hans)
  • Slovak (sk)
  • Slovenian (sl)
  • Spanish (es)
  • Swedish (sv)
  • Traditional Chinese (zh-Hant)
  • Turkish (tr)
  • Ukrainian (uk)

Last update: June 7, 2022