Android documentation

The MOTIONTAG Mobility & Location Analytics SDK enables to collect raw sensor data of the telephone in a battery efficient way. This data is then transmitted to the MOTIONTAG back-end system (ISO 27001 certified). In the backend, the sensor events are processed and a partial journey is constructed. Journeys consist either solely of tracks or tracks plus stays. Tracks describe a movement from a origin to a destination with a certain mode of transport. Stays symbolize a stationary behaviour with a particular purpose for the visit.

The use cases for the SDK are manifold. In the mobility sector it can be used to get detailed mobility data for planning purposes. The collected data enables to compare how the transport network is being used. This way the effectiveness of the current infrastructure and the passenger flow management is measured and the design of new mobility services. By implementing and using the SDK you can make use of these findings to improve timetables and routes, expand transport supply and attract more passengers.

If you integrate the MOTIONTAG SDK inside your own application, you can either download user journeys via a provided dump interface on the internet or we tailor a customized solution to your needs.

1. Download

1.1 Native sample app

A native Android sample app can be found on our GitHub page: https://github.com/MotionTag/motiontag-sample-app-android This sample app showcases the best practices in integrating the SDK into native Android apps.

1.2 Project build gradle

First you need to add the following to you projects build.gradle file:

allprojects {
    repositories {
        maven {
            url "https://artifactory.motion-tag.de/artifactory/libs-release"
            credentials {
                username = "${ARTIFACTORY_USERNAME}"
                password = "${ARTIFACTORY_PASSWORD}"
            }
       }
   }
}

This allows you to download the SDK from our custom maven repository.

1.3 Global gradle properties

Additionally you have to add your credentials inside your system's global gradle.properties file:

ARTIFACTORY_USERNAME=<YOUR_USERNAME>
ARTIFACTORY_PASSWORD=<YOUR_PASSWORD>

On Linux you find this file usually under ~/.gradle/gradle.properties. Please contact us to get your credentials.

1.4 Application build gradle

The SDK requires compileSdkVersion 28 or higher for on version 2.1.0 or older and compileSdkVersion 29 on version 2.2.0+

Java 8 support is needed, therefore is should be added via compileOptions:

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }
}

It also requires apps to migrate to androidx, you can check the official migration guide here.

Lastly, you need to include the following dependency in your application build.gradle file to download the SDK:

implementation 'de.motiontag:tracker:<VERSION>'

1.5 Auto Backup for Apps

Auto Backup for Apps is a platform feature that automatically backs up a user's data from apps that target and run on Android 6.0 (API level 23) or later. To limit unexpected behavior from our SDK, you should either disable automated backups entirely or exclude the appropriate SDK files from full backup. To fully disable the Auto Backup feature you must add the following flag to the app's AndroidManifest.xml :

<manifest ... >
    ...
    <application
        android:allowBackup="false" ... >
    </application>
    ...
</manifest>

In case your app relies on Auto Backup and you don't want fully disable it, some SDK files must be excluded from the process:

<manifest ... >
    ...
    <application
        android:allowBackup="true"
        android:fullBackupContent="@xml/my_backup_rules" ... >
    </application>
    ...
</manifest>

The my_backup_rules.xml is a xml file that should be created in the app's resources folder and contains the paths that must be excluded from the back up process:

<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
    <exclude domain="sharedpref" path="motiontag_tracker.xml"/>
    <exclude domain="database" path="motiontag_tracker.db"/>
    <exclude domain="database" path="motiontag_tracker.db-shm"/>
    <exclude domain="database" path="motiontag_tracker.db-wal"/>
    <exclude domain="database" path="motiontag_tracker.db-journal"/>
</full-backup-content>

The above lines can be appended to an existing file in case your app has already defined one. More information can be found here.

1.6 SDK version

You can find the SDK version in our changelog page.

You should be good to go now ;)

2. MotionTag Tracker Structure

2.1 MotionTag class

The MotionTag class is the main entry point of the SDK. It has the following public methods:

Returns Method Explanation
void with(application: Application, settings: Settings, callback: MotionTag.Callback) Initializes the SDK with the Application context, Settings and the MotionTag.Callback implementation.
void start(token: String) Starts tracking for the specified user JWT token. The provided MotionTag.Callback implementation will be called to inform about SDK state changes or relevant tracking events.
void stop() Stops tracking and removes the foreground notification.
Boolean isTrackingActive() Returns true if the tracking is active and collecting data, false otherwise.
void notification(notification: Notification) Updates the provided tracking Notification. Can be safely called even after the tracking has been started.
void useBatterySavingMode(on: Boolean) Enables the Battery Saving Mode, as known as Slow-Mode. In this mode the Location Services is disabled to preserve battery when the phone is stationary after a certain amount of time. Disabling this mode leads to a higher battery usage, however it improves the location tracking. Can be safely called even after the tracking has been started.
void useWifiOnlyDataTransfer(on: Boolean) Enables the data transmission only when connected to Wifi. Can be safely even called after the tracking has been started.
Boolean hasRequiredLocationSettings() Checks if the device has all the required location settings (e.g.: LocationManager.GPS_PROVIDER).
Boolean hasRequiredPermissions() Checks if the app has been granted all required runtime permissions.
List<String> getRequiredPermissions() Returns an list with the required runtime permissions.
List<String> getDeniedRequiredPermissions() Returns an list with the required runtime permissions that are still denied.
void requestRequiredLocationSettings(activity: Activity, requestCode: Int) Displays a dialog that allows users to enable all the required location settings. The result of the dialog will be returned via onActivityResult(requestCode: Int, resultCode: Int, data: Intent).
Settings getSettings() Retrieves current settings
void clearData() Stops tracking and deletes all stored user data from the SDK

2.2 Settings.Builder class

The Settings class is used to customise the behaviour of the SDK during its initialization.

Returns Method Explanation
Settings.Builder notification(notification: Notification) Sets the Android Notification to be displayed when the device is moving. The FLAGONGOINGEVENT flag will be added to the Notification if not provided. Starting from API version 26, a NotificationChannel must be created first. In order to avoid distracting the user, we recommend setting NotificationCompat.PRIORITY_LOW and NotificationManager.IMPORTANCE_LOW to your Notification and the associated NotificationChannel, respectively.
Settings.Builder useBatterySavingMode(on: Boolean) Enables the Battery Saving Mode, so less battery is draining if set to true. In this mode the Location Requests are disabled when stationary to preserve battery. Disabling this mode leads to a higher battery usage, however it improves the location tracking. The default is set to true.
Settings.Builder useWifiOnlyDataTransfer(on: Boolean) Enables Wifi only transmission of sensor events. Disabling this can lead to high cost because of the mobile data usage, however it improves analysis speed, so the user has maybe a faster response of processed journeys. The default is set to true.
Settings build() As described in the Builder Pattern a build() method is needed to construct the actual Settings object.

2.3 MotionTag.Callback interface

MotionTag.Callback interface used to inform an application about state changes:

Returns Method Explanation
void onEvent(event: Event) Informs the application about a new incoming event.

Event description:

Event Explanation
AutoStartEvent(reason: Reason) Informs the application that tracking has been automatically started.
AutoStopEvent(reason: Reason) Informs the application that tracking has been automatically stopped.
LocationEvent(location: Location) Hands the latest captured android.location.Location. to the application.
TransmissionEvent.Success(timestamp: Long, trackedFrom: Long, trackedTo: Long) Informs when a package of events has been successfully sent to the server. The timestamp represents the time of the server confirmation response. The trackedFrom and trackedTo represents the time range of the transmitted events.
TransmissionEvent.Error(timestamp: Long, errorCode: Int, errorMessage: String) Informs when a package of events failed to be transmitted to the server. The timestamp represents the time of the failure. The errorCode and errorMessage describes the error that occurred.

2.4 Reason enum

The Reason enum describes the reasons behind the automatic tracking stops and starts.

Selection Explanation
RESTART This is the case when tracking is restarted after a phone restart.
LOCATION_SERVICE This is the case when tracking is restarted after the user turns on the location services.
KILLED This is the case when tracking is restarted after an application kill.
PERMISSION This is the case when tracking is started without the required runtime permissions.

3. Usage

3.1 Initialization

This initialization should be either in your Application file or in a file that is directly affiliated with the Application, so it's started/restarted when the Application file is initialized.

class App : Application(), MotionTag.Callback {

    private val yourNotification: Notification // https://developer.android.com/training/notify-user/build-notification

    override fun onCreate() {
        val settings = Settings.Builder()
            .notification(yourNotification)  // Your notification that should be shown while tracking is active
            .useBatterySavingMode(true)      // default: true
            .useWifiOnlyDataTransfer(true)   // default: true
            .build()
        MotionTag.with(this, settings, this)
    }

    override fun onEvent(event: Event) {
        TODO() //Handle events from the SDK
    }
}

3.2 Runtime permissions

3.2.1 Android 9 and below

The SDK needs the android.permission.ACCESS_FINE_LOCATION runtime permission.

3.2.2 Android 10

The SDK needs android.permission.ACCESS_FINE_LOCATION permission and since Android 10 both android.permission.ACCESS_BACKGROUND_LOCATION and android.permission.ACTIVITY_RECOGNITION are also needed.

After requesting the location permissions, Android 10 users will get a dialog with 3 different options to choose from. Users must select "Allow all the time" option, otherwise the SDK won't function properly.

3.2.3 Android 11

Starting on Android 11, the android.permission.ACCESS_FINE_LOCATION permission must be granted before requesting android.permission.ACCESS_BACKGROUND_LOCATION. There are 2 different cases to consider when running the SDK on a Android 11 device:

  • If the app targets Android 11 (targetSdkVersion = 30): A system exception will be thrown if android.permission.ACCESS_BACKGROUND_LOCATION is requested before holding android.permission.ACCESS_FINE_LOCATION permission.
  • If the app targets Android 10 or lower (targetSdkVersion <= 29): The app can still request both location permissions at the same time. The resulting permission dialog will contain a link that allows users to select "Allow all the time" in the app permission settings.

More information in the official Android documentation. An example implementation can be found in our sample app.

3.2.4 Runtime permission helper functions

The SDK exposes 3 public methods that helps you verify whether the SDK has been granted or not all required runtime permissions based on the device's Android version:

hasRequiredPermissions(): Bool, getRequiredPermissions(): List<String>, getDeniedRequiredPermissions(): List<String>.

3.3 Location settings

Location services should be enabled in the device otherwise the SDK will not function properly. In order to display a dialog requesting the users to enable it, one could use the SettingsClient and set LocationRequest.PRIORITY_HIGH_ACCURACY priority in the LocationSettingsRequest.Builder.

The SDK contains 2 methods to help you with the task of verifying and requesting the required location settings: boolean hasRequiredLocationSettings() and requestRequiredLocationSettings(Activity activity, int requestCode).

3.4 Start/Stop Tracking

To start tracking, you need a token for the SDK to access our backend system. Please get in touch and we will provide you with your token as soon as possible.

Using the token you can start...

class CustomClass : MotionTag.Callback {

    private val yourToken = "SECRET_JWT"

    fun start() {
        MotionTag.start(yourToken)
    }
}

... and stop

class CustomClass : MotionTag.Callback {

    fun stop() {
        MotionTag.stop()
    }
}

... the tracking after the initialization from wherever you want.

3.5 Automatic Start/Stop

When the tracking is ACTIVE it will be automatically stopped and in some cases automatically restarted e.g.

  • Location Services Off causes an automatic stop (Reason.LOCATION_SERVICE).
  • Location Services On causes an automatic start (Reason.LOCATION_SERVICE).
  • Reboot causes an automatic start (Reason.RESTART).
  • Application Update causes an automatic start (Reason.RESTART).
  • Missing a required permission causes an automatic stop (Reason.PERMISSION).
  • System or user kills the App causes an automatic start. If the App is manually opened by the user after the killing, the tracking will be immediately started again. Otherwise, the tracking will be automatically restarted at most 20 minutes. (Reason.KILLED).
class CustomClass : MotionTag.Callback {

    override fun onEvent(event: Event) {
        when (event) {
            is AutoStartEvent -> onAutoStart(event.reason) // do something after the tracking was restarted, e.g.
            is AutoStopEvent -> onAutoStop(event.reason)   // do something after the tracking was stopped, e.g.
            is TransmissionEvent -> onTransmission(event)  // do something after the application has successfully transmitted data, or an error occurred in the process e.g.
            is LocationEvent -> onLocation(event.location) // do something after the application provides a location, e.g.
        }
    }

    private fun onAutoStart(reason: Reason) {
        when (reason) {
            Reason.RESTART -> TODO()           // do something after the tracking was restarted after rebooting the phone or updating the application.
            Reason.LOCATION_SERVICE -> TODO()  // do something after the tracking was restarted by turning on the location services.
            Reason.KILLED -> TODO()            // do something after the application has been killed and the tracking is restarted.
        }
    }

    private fun onAutoStop(reason: Reason) {
        when (reason) {
            Reason.RESTART -> TODO()            // do something after the tracking was stopped because the phone was shut down
            Reason.LOCATION_SERVICE -> TODO()   // do something after the tracking was stopped by turning off the location services.
        }
    }

    private fun onTransmission(event: TransmissionEvent) {
        when(event) {
            is TransmissionEvent.Success -> TODO()   // Transmission was successful. Maybe display the time, so the user gets informed that data will be analysed soon.
            is TransmissionEvent.Error -> TODO()     // Transmission was not successful. Maybe log the error in order to analyze it afterwards.
        }
    }

    private fun onLocation(location: Location) {
        // Maybe display the location on a map
    }
}

3.6 Runtime Setting Changes

You can still change all settings during runtime. For example if the user changes the language settings on the phone you probably want to provide a new notification with the appropriate language. Just call one of the following methods from anywhere in your application.

class CustomClass : MotionTag.Callback {

    private val yourNotification: Notification // https://developer.android.com/training/notify-user/build-notification

    fun customize() {
        MotionTag.useWifiOnlyDataTransfer(true)
        MotionTag.useBatterySavingMode(false)
        MotionTag.notification(yourNotification)
    }
}

3.7 Current Settings

You can get current SDK settings with the following call:

class CustomClass : MotionTag.Callback {

    fun getCurrentSettings() : Settings {
        return MotionTag.getSettings()
    }
}

3.8 Clear stored user data

It is possible to delete user data that is stored locally with the following command...

class CustomClass : MotionTag.Callback {

    // more implementation specifics

    fun clearUserData() {
        MotionTag.clearData()
    }
}

Executing this function also stops tracking. This is particularly useful to delete user data when he logs out from the app.

4. Phones

4.1 Non-standard background process limitations

Some Android OEMs, like Huawei and OnePlus, decided to implement non-standard background process limitations on 3rd party apps as an attempt to reduce battery consumption. The MOTIONTAG SDK must be running all times in the background, otherwise it won't function properly.

Therefore we recommend developers integrating our SDK to read the https://dontkillmyapp.com website, it describes this problem in detail and it provides some workaround options for both developers and users. There's also this StackOverflow post which describes how developers can forward users to the correct OEM settings.

When battery optimization is turned on for your app, the MOTIONTAG SDK may not be able to track and generate data continuously. If 24/7 tracking on all supported phone models is crucial to your use case, we strongly recommend you to include a prompt for the user, and facilitate the deactivation of battery optimization settings for your app on the affected phones.

5. Gradle

  • Minimum SDK Version: 18
  • Obfuscated with R8

The MOTIONTAG SDK relies on Free and Open Source software components. These components are available under the following licenses: