Client Usage¶
The Client Usage classes allow you to enable your mobile applications to collect and upload client usage data. Administrators can use the SAP mobile service cockpit to view reports or download client usage data and create custom reports using this data. See Usage Reporting in the mobile services documentation for more information on the server and its functionality.
There are three options for managing sessions:
-
UsageService - A facade introduced in SAP BTP SDK for Android version 3.0.0. It builds on the two layers below to provide a simpler way to manage the sessions, usage upload, and other tasks.
-
UsageBroker - A higher-level convenience helper that builds on
AppUsage
. It simplifies the initialization, collection, storage, and uploading of usage data. It can be enabled to automatically capture lifecycle events such as "Session/Event start/stop". TheUsageBroker
is configurable for a multi-user environment within an application. -
AppUsage - Works with direct access to the lower-level objects. This option provides the ability to directly control each component but requires greater effort to set up and maintain.
Note
Only one usage provider should be initialized at a time.
Using the UsageService
Facade¶
UsageService
provides auto-session and auto-upload features, which are both enabled by default, and it also
defines a default usage store name and the default threshold of usage records (10KB) to trigger the auto-upload
process. If you want to use a different store name, or don't want to use the auto-upload feature, you can use the APIs before providing the UsageService
instance to the SDKInitializer.start
method for initialization.
public UsageService setStoreName(@NonNull UUID storeName) {...}
public UsageService setAutoSession(@NonNull boolean autoSession) {...}
public UsageService setAutoUpload(@NonNull boolean autoUpload) {...}
public UsageService setUploadThresholdKB(@NonNull int sizeOfKB) {...}
The client code may look like:
UsageService usageService = new UsageService();
usageService
.setStoreName(UUID.fromString("xxxxxxx"))
.setAutoSession(true)
.setUploadThresholdKB(20);
SDKInitializer.INSTANCE.start(application, new MobileService[]{usageService}, null);
val usageService = UsageService().apply {
this.setStoreName(UUID.fromString("xxxxxxx"))
.setAutoSession(true)
.setUploadThresholdKB(20)
}
SDKInitializer.start(application, usageService)
For the auto-session feature, session start and end events will be recorded when the application is brought up to
the foreground and put into the background respectively. If you want to manage your own usage session, you can disable the auto-session feature in UsageService
, then call the 2 methods below to start/end the usage session in your application. If
you want to use the auto upload feature, please use the method recordSessionEnd
below instead of the corresponding
version in AppUsage
.
fun recordSessionStart(context: Context) { ... }
fun recordSessionEnd(context: Context) { ... }
If you want to use the auto-upload feature without the auto-session feature, please make sure to call the above method when recording the session end.
UsageService
also provides APIs to help the client code log events.
fun logDeviceInfoEvents(context: Context) { ... }
fun eventBehaviorUserInteraction(
viewId: String, elementId: String? = null,
action: String? = null, interactionValue: String? = null
) { ... }
fun eventBehaviorViewDisplayed(
viewId: String, elementId: String? = null,
action: String? = null, interactionValue: String? = null
) { ... }
fun eventStart(info: AppUsageInfo, type: String? = null, key: String? = null) { ... }
fun eventEnd(info: AppUsageInfo, type: String? = null, key: String? = null) { ... }
fun event(
info: AppUsageInfo, type: String? = null, key: String? = null, duration: Long? = null
) { ... }
The table below provides an overview of the APIs.
API | Description |
---|---|
logDeviceInfoEvents |
Logs the device-related information: screen size, orientation, memory, etc. |
eventBehaviorUserInteraction |
Logs an action performed by the users: button clicks, menu item clicks, etc. |
eventBehaviorViewDisplayed |
Logs the screen navigations, for example when an Activity is being displayed. |
eventStart |
Starts a custom event. When used together with eventEnd , the duration of this event will automatically be recorded. |
eventEnd |
Ends a custom event. The duration of the event will automatically be recorded if eventStart with the same key was called before. |
event |
Logs a custom event. The client code is responsible for the duration of this event. |
Using the UsageBroker
Convenience Helper¶
To collect, store, and upload client usage event data:
- Start the broker. Add this call to the application's
onCreate()
. - Collect any events. Additional analytic data can be captured and reported to the server using the
AppUsage
class. - Call Upload (anywhere and often). It will only upload at the requested frequency.
Start UsageBroker
¶
try {
// In your Application class, start the UsageBroker
UsageBroker.start(this, getApplicationContext(), "1.0", true);
} catch (Exception ex) {
sLogger.error(ex.getMessage());
}
try {
// In your Application class, start the UsageBroker
UsageBroker.start(this, getApplicationContext(), "1.0", true)
} catch (Exception ex) {
sLogger.error(ex.getMessage())
}
Multi-User Support for UsageBroker
¶
When developing a multi-user app, the UsageBroker
can be reconfigured on a per-user basis. This introduces the concept
of "unattributed" and "attributed" stores. The unattributed store is used to capture all usage events when a user has
not been onboarded. The attributed store is used for all usage events once a user has been onboarded successfully.
By calling one of the provided configure()
methods, you can reconfigure the UsageBroker
to switch between existing and
new users. When moving to an attributed user, call the configure()
method passing in a UUID (Universal Unique
Identifier). This creates or opens the store associated with this UUID, which should uniquely identify a user.
Likewise, to switch to an unattributed user, call one of the configure()
overloads that does not specify a UUID. You
can also optionally pass in an encryption key when calling configure for an attributed user. This key should remain
constant after the initial call, otherwise an OpenFailureException
is thrown.
The retainLastUnattributedSession
parameter defaults to true if not provided when calling configure()
. This is
useful for scenarios when a user is being onboarded. When configure()
is called and you are switching from an
unattributed store to an attributed store, this will take all the events from the beginning of the onboarding session
and transfer them to the newly onboarded user's usage store.
// Configure UsageBroker for an attributed user
try {
UsageBroker.configure(context, uuid);
} catch (OpenFailureException | EncryptionError e) {
// Handle Error
}
try {
UsageBroker.configure(context, uuid)
} catch (e: OpenFailureException) {
// Handle error
} catch (e: EncryptionError) {
// Handle error
}
Collect Any Events¶
See Reporting Event Metrics for more information about custom events.
// Single event call.
AppUsage.event(
"eventType1", // Event type, optional.
"testEvent1Key", // Event key. When not specified, `other` will be used.
4L, // Duration.
new AppUsageInfo() // Event metrics using AppUsageInfo.
.screen("firstScreen")
.value("randomValue")
.category("category1");
// Single event call.
AppUsage.event(
"eventType1", // Event type, optional.
"testEvent1Key", // Event key. When not specified, `other` will be used.
4L, // Duration.
AppUsageInfo() // Event metrics using AppUsageInfo.
.screen("firstScreen")
.value("randomValue")
.category("category1"))
See AppUsage
Behavior Events for more information about Behavior events.
if(AppUsage.isInitialized()){
AppUsage.eventBehaviorViewDisplayed("MainActivity", "", "", "");
}
if(AppUsage.isInitialized()){
AppUsage.eventBehaviorUserInteraction("MainActivity","FloatingActionButton","Button Pressed", "");
}
if (AppUsage.isInitialized()) {
AppUsage.eventBehaviorViewDisplayed("MainActivity", "", "", "")
}
if (AppUsage.isInitialized()) {
AppUsage.eventBehaviorUserInteraction("MainActivity", "FloatingActionButton", "Button Pressed", "")
}
Upload Using UsageBroker
¶
Uploading using the UsageBroker
gives you convenience mechanisms that are not available when using the underlying
components. The UsageBroker
can control upload frequency, as well as the ability to upload an attributed and
unattributed usage store together.
When uploading using the UsageBroker
, the sendUnattributed
flag is set to true by default unless specified in the
appropriate method call. The sendUnattributed
flag determines whether to upload the unattributed store. If configured
on an attributed store, the flag uploads both the attributed store and the unattributed store. If configured on the
unattributed store, this flag has no effect.
AppUsageUploader.addUploadListener(new AppUsageUploader.UploadListener() {
@Override
public void onSuccess() {
sLogger.debug("Usage upload complete, time taken (in nanos): " + (System.nanoTime() - startTime));
}
@Override
public void onError(Throwable error) {
Toast.makeText(getActivity(), "Upload Err:\n"+error.getMessage(), Toast.LENGTH_LONG).show();
if (error instanceof HttpException) {
sLogger.debug("Upload server error: {}, code = {}",
((HttpException) error).message(), ((HttpException) error).code());
} else {
sLogger.debug("Upload error: {}", error.getMessage());
}
}
@Override
public void onProgress(int i) {
sLogger.debug("Usage upload progress: " + i);
}
});
UsageBroker.upload(false, true, getApplicationContext());
AppUsageUploader.addUploadListener(object : AppUsageUploader.UploadListener {
override fun onSuccess() {
sLogger.debug("Usage upload complete, time taken (in nanos): " + (System.nanoTime() - startTime))
}
override fun onError(error: Throwable) {
Toast.makeText(activity, "Upload Err:\n" + error.message, Toast.LENGTH_LONG).show()
if (error is HttpException) {
sLogger.debug("Upload server error: {}, code = {}",
error.message(), error.code())
} else {
sLogger.debug("Upload error: {}", error.message)
}
}
override fun onProgress(i: Int) {
sLogger.debug("Usage upload progress: $i")
}
})
UsageBroker.upload(false, true, getApplicationContext())
Using Lower-Level Components¶
Sessions¶
For the purposes of reporting, define a user session as the duration the mobile application is in the foreground. The session acts as a container for event metrics data. In addition, it contains information such as:
- Device platform (Android/iOS)
- Device platform version
- Device Id
- Application Id
- Application version
Event Metrics Data¶
An application can report event metrics data using AppUsageInfo
. SAP Mobile Services supports a standard schema to collect
event metrics. The values for the metrics and how to interpret them for a custom report is up to your mobile application
designers and product managers.
The AppUsageInfo
class consists of the following fields and can collect metrics for the following events:
Field | Description |
---|---|
Type |
The event type |
Key |
The key to identify the event |
Action |
The action associated with the event |
Behavior |
User-defined behavior properties of the event |
Category |
User-defined event category type |
Duration |
The duration of the event in milliseconds |
Element |
The SDK/UI element associated with the event |
Measurement |
User-defined measurement of the event, such as UI element size |
Others |
Any other user-defined properties associated with the event |
Result |
User-defined result associated with the event. This is usually used with Action . |
Screen |
The properties of the screen |
Case |
User-defined case |
Unit |
The unit associated with Measurement |
Value |
User-defined value properties |
View |
Android View related properties |
Reporting¶
During the reporting phase, client data is collected and persisted in an encrypted store on the device. The data can then be uploaded to the mobile services server and viewed in client usage reports. See the uploading section in this topic for more information.
Reporting involves:
- Initializing the
AppUsage
Class - Recording the Session Start
- Reporting Event Metrics
AppUsage
Behavior Events- Recording the Session End
Initializing the AppUsage
Class¶
A one-time initialization is required. An encryption key is required to encrypt the underlying persistence store.
We recommend that you use the encryption utility to obtain the encryption key. If it is the first time AppUsage
is
being configured and null is provided as the encryption key, a key is generated transparently and is used for
subsequent configure
calls. See Encryption Utility for more information.
This must occur before any AppUsage
related calls occur.
// SettingsParameter is retrieved from SettingsProvider class and application Id and version in SettingsParameter will be
// used during reporting and uploading to form the mobile services URL.
// Encryption key is used to encrypt the underlying persistence store.
byte[] encrytionKey = EncryptionUtil.getEncryptionKey("aliasForAppUsage", passcodeFromUser);
try {
AppUsage.initialize(appContext, "myUsageStore", encrytionKey);
// Or passes null and the AppUsage will generate an encryption key to encrypt the persistence store
// and subsequent `initialize` calls.
// AppUsage.initialize(appContext, "myUsageStore", settingsParameters, null);
} catch (OpenFailureException ex) {
logger.error("Failed to open Usage store.", ex);
}
// SettingsParameter is retrieved from SettingsProvider class and application Id and version in SettingsParameter will be
// used during reporting and uploading to form the mobile services URL.
// Encryption key is used to encrypt the underlying persistence store.
val encrytionKey = EncryptionUtil.getEncryptionKey("aliasForAppUsage", passcodeFromUser)
try {
AppUsage.initialize(appContext!!, "myUsageStore", encrytionKey)
// Or passes null and the AppUsage will generate an encryption key to encrypt the persistence store
// and subsequent `initialize` calls.
// AppUsage.initialize(appContext, "myUsageStore", settingsParameters, null);
} catch (ex: OpenFailureException) {
logger.error("Failed to open Usage store.", ex)
}
Recording the Session Start¶
Reporting must begin with a sessionStart
and end with a sessionEnd
. Any calls to event reporting without a
sessionStart
are ignored. Event metrics reporting using eventStart
, eventEnd
, and event
must happen after
calling sessionStart
and before sessionEnd
is called.
A call to sessionStart
marks the start of a session with the session-related attributes such as "Platform" and
"Platform Version". Consecutive calls to sessionStart
without calling sessionEnd
ends the current session and
creates a new session.
AppUsage.sessionStart();
AppUsage.sessionStart()
Reporting Event Metrics¶
After a sessionStart
is called, one or more event records can be reported. Events can be reported using a single call
to event()
or a pair of eventStart()
and eventEnd()
calls. In the single call, your application is responsible for
providing duration information if applicable. In the paired call, the event duration is automatically calculated.
Each of the event metrics fields accepts a string value. It is up to you to decide what value should be recorded in each of the fields.
AppUsage.eventStart("filterEventType", "productFilter",
new AppUsageInfo()
.screen("Product Filter")); // Uses one or more of the event metrics methods-- screen, element, view,
// category, etc.
AppUsage.eventEnd("filterEventType", "productFilter",
new AppUsageInfo()
.screen("Product Filter"));
// Single event call.
AppUsage.event(
"eventType1", // Event type, optional.
"testEvent1Key", // Event key. When not specified, `other` will be used.
4L, // Duration.
new AppUsageInfo() // Event metrics using AppUsageInfo.
.screen("firstScreen")
.value("randomValue")
.category("category1");
AppUsage.eventStart("filterEventType", "productFilter",
AppUsageInfo()
.screen("Product Filter")) // Uses one or more of the event metrics methods-- screen, element, view,
// category, etc.
AppUsage.eventEnd("filterEventType", "productFilter",
AppUsageInfo()
.screen("Product Filter"))
// Single event call.
AppUsage.event(
"eventType1", // Event type, optional.
"testEvent1Key", // Event key. When not specified, `other` will be used.
4L, // Duration.
AppUsageInfo() // Event metrics using AppUsageInfo.
.screen("firstScreen")
.value("randomValue")
.category("category1"))
AppUsage
Behavior Events¶
Analytic Behavior events simplify the consistent capturing of two categories of event types: "View Displayed" and "User Interaction".
- The "View Displayed" method is called by the developer to record that a particular UI control is displayed
(
MainActivity
, for example). It only requires the view name/id.
AppUsage.eventBehaviorViewDisplayed(...)
¶
if (AppUsage.isInitialized()){
AppUsage.eventBehaviorViewDisplayed("MainActivity", "", "", "");
}
if (AppUsage.isInitialized()) {
AppUsage.eventBehaviorViewDisplayed("MainActivity", "", "", "")
}
- The "User Interaction" method is called to record some type of user interaction (for example, a button click or selecting a row).
AppUsage.eventBehaviorUserInteraction(...)
¶
if (AppUsage.isInitialized()){
AppUsage.eventBehaviorUserInteraction("MainActivity","FloatingActionButton","Button Pressed", "");
}
if (AppUsage.isInitialized()) {
AppUsage.eventBehaviorUserInteraction("MainActivity", "FloatingActionButton", "Button Pressed", "")
}
Recording the Session End¶
A call to sessionEnd
marks the end of a session.
AppUsage.sessionEnd();
AppUsage.sessionEnd()
Uploading¶
Before using AppUsageUploader
, enable the upload of application-specific usage statistics and reports from mobile
devices using the SAP mobile service cockpit. See Defining Usage Report
Policy.
AppUsageUploader
retrieves the usage data reported prior to the last session end from the underlying persistence
store, converts them to the format required by the mobile service, and then uploads them.
Any events that are in the current open session are uploaded on the next upload call.
Registering the Upload Listener¶
To be notified whether the upload succeeds or fails, or to get upload progress, implement
AppUsageUploader.UploadListener
. This is an optional but recommended step.
Note that all callback methods are invoked in UI(main) thread.
// All callback methods will be invoked in UI(main) thread.
class MyUploadListener implements AppUsageUploader.UploadListener {
@Override
public void onSuccess() {
logger.debug("Usage Upload completed successfully");
// For example, put a toast message.
}
@Override
public void onError(Throwable error) {
if (error instanceof HttpException) {
logger.debug("Usage Upload server error: {}, code = {}", ((HttpException) error).getMessage(),
((HttpException) error).getCause());
} else {
logger.debug("Usage Upload error: {}", error.getMessage());
}
}
@Override
public void onProgress(int percentage) {
// For example, updates the progress dialog/bar.
}
}
MyUploadListener myUploadListener = new MyUploadListener();
internal var myUploadListener = MyUploadListener()
// All callback methods will be invoked in UI(main) thread.
internal inner class MyUploadListener : AppUsageUploader.UploadListener {
override fun onSuccess() {
logger.debug("Usage Upload completed successfully")
// For example, put a toast message.
}
override fun onError(error: Throwable) {
if (error is HttpException) {
logger.debug("Usage Upload server error: {}, code = {}", error.message,
error.cause)
} else {
logger.debug("Usage Upload error: {}", error.message)
}
}
override fun onProgress(percentage: Int) {
// For example, updates the progress dialog/bar.
}
}
Register the listener by calling:
AppUsageUploader.addUploadListener(myUploadListener);
AppUsageUploader.addUploadListener(myUploadListener)
If automatic upload is enabled by UsageService
and triggered at some time, the listeners will be notified too.
Starting the Upload¶
To start the upload, call upload
from the UI(main) thread. All usage data retrieval and uploading is performed in an
AsyncTask
.
Note
Make sure the mobile application has already authenticated with the server before the upload
call. This call
requires authentication.
AppUsageUploader.upload();
AppUsageUploader.upload()
Viewing Usage Reports on the Server¶
After a successful client data upload, you can view the data in the SAP mobile service cockpit. See Viewing the Client Data Report.