Developer Reference#

Android API reference

Protection Status Updates#

The SDK exposes a data structure named LRProtectionStatus that contains the SDK state. You can get it at any time in a synchronous fashion, or subscribe to receive events asynchronously anytime the status changes. The fields in the LRProtectionStatus structure are as follows.

isProtectionActive: Boolean#

Whether a protection session is running.

vehicleType: LRVehicleType?#

Available only when isProtectionActive is true. This reflects the parameter that was passed to startProtection().

Values:

  • on Android, MOTORCYCLE, BICYCLE or CAR

  • on iOS, .motorcycle, .bicycle or .car

sessionId: String?#

Available only when isProtectionActive is true. This is a unique identifier for this session, generated by the SDK.

hasShockAnalysis: Boolean#

This field is for advanced use cases and might be removed in the future.

alert: LRAlert?#

When the session starts, this field is initially null. When an accident is detected, a countdown can be shown to the user to allow them to abort the accident process before an emergency is sent. This structure contains more information about this part of the process.

alertStartTime#

  • on Android, this is a Long

  • on iOS, this is a Date

alertDuration#

  • on Android, this is a Long

  • on iOS, this is a Int

emergency: LREmergency?#

When the session starts, this field is initially null. If the user does not stop the accident process during the countdown, the SDK will begin sending the emergency to SafeRider servers. This structure contains more information about this part of the process. See below for the contents.

emergencyId: String#

isEmergencySending: Boolean#

hasEmergencySendingFailed: Boolean#

isEmergencySent: Boolean#

isEmergencyCanceled: Boolean#

isEmergencyEnded: Boolean#

Fall survey answer#

After an accident alert is started, a survey can be displayed by the default screens (see US#5 - Quick Survey to cancel the Emergency). The SDK exposes a data structure named LRFallSurveyAnswer that contains the answer to the fall survey if you are using the included accident screens. It has only one field:

hasFallen: Boolean#

The user answer.

Android#

In order to be notified when the user answers the fall survey, you can set a listener for a specific SDK event:

SafeRider.sharedInstance.fallSurveyListener = object : SafeRider.FallSurveyListener {
   override fun onAnswered(hasFallen: Boolean) {
       // here handle user fall survey answer
   }
}

iOS#

In order to be notified when the user answers the fall survey, you have to listen the fall survey answer.

Using the LRSaferiderDelegationProtocol :

self.safeRiderInstance.protectionDelegate = self

extension MyClass: LRProtectionDelegate {

    func onFallSurveyAnswer(_ fallSurveyAnswer: LRFallSurveyAnswer?) {
        log.debug("user has fallen: \(fallSurveyAnswer.hasFallen)")
    }
}

Using the LRSaferiderReactiveProtocol :

var cancellableBag = Set<AnyCancellable>()

self.safeRiderInstance.fallSurveyAnswerPublisher
    .sink { fallSurveyAnswer in
        log.debug("user has fallen: \(fallSurveyAnswer.hasFallen)")
    }
    .store(in: &self.cancellableBag)

SDK Methods#

initialize()#

Android#

initialize(context: Context, apiKey: String, notification: LRNotification? = null, tripDetectionNotification: LRNotification? = null, showAccidentUi: Boolean = true, accidentUiTextToSpeech: String? = null, accidentUiScreenLogo: Int? = nil)

iOS#

static func initialize(apiKey: String, showAccidentUi: Bool = true, accidentUiTextToSpeech: String? = nil, accidentUiScreenLogo: UIImage? = nil, accidentUiScreenLogoDarkMode: UIImage? = nil) throws

Initialize the SDK with your current api key. This method must be called from your application onCreate() method (Android) or AppDelegate didFinishLaunchingWithOptions() (iOS). All the other methods require that the SDK has been initialized, otherwise they will instantly throw an error.

Initialization throws an error if the api key provided is not valid :

  • on Android: UninitializedPropertyAccessException

  • on iOS: LRProtectionError.invalidApiKey

Set showAccidentUi to false to disable the included accident screens. In this case you need to implement the screens yourself following the specifications in Typical App User Stories.

If showAccidentUi is set to true, you could:

  • set accidentUiTextToSpeech to customize the message that is sent to speech synthesis during an accident. By default it will be “Accident alert”

  • set your rectangular logo to accidentUiScreenLogo: it will be displayed at the top of all the accident workflow’s screens

  • (on iOS only) set your dark mode rectangular logo accidentUiScreenLogoDarkMode too if your app manages the dark mode and your accidentUiScreenLogo is not optimized for it.

NB: if you do not set at least accidentUiScreenLogo in this case, it will be the Liberty Rider’s logo that will be displayed on relative screens.

For explanation on the notification parameter on Android, please refer to Notification for Foreground Service. Set a custom notification that will be used by the SDK to bind its “foreground service”, as required since Android 8 to allow applications to run in background. This parameter is not available on iOS.

startProtection()#

Android#

startProtection(vehicleType: LRVehicleType, userId: String, phoneNumber: String, firstName: String?, lastName: String?, metadata: Map<String, Any>?) : LRStartProtectionResult

iOS#

Using the LRSafeRiderDelegationProtocol :

func startProtection(vehicleType: LRVehicleType, userId: String, phoneNumber: String, userFirstName: String?, userLastName: String?, metadata: [String : Any]?)

Using the LRSafeRiderReactiveProtocol :

func startProtection(vehicleType: LRVehicleType, userId: String, phoneNumber: String, userFirstName: String?, userLastName: String?, metadata: [String : Any]?) -> AnyPublisher<Void, LRProtectionError>

Start a new protection session. Parameters:

  • vehicle type which allows to choose between .motorcycle or .bicycle support

  • user identity, which will be used by our operators and emergency services:
    • phone number (mandatory)

    • unique identifier of the user account (mandatory)

    • first name

    • last name

  • some metadata (optional) for this session or ride. It can be used to link ride data from your information system to a specific emergency on SafeRider servers.

The SDK needs some guarantees before starting a session, otherwise it will throw an error instantly:

  • You need to have initialized the SDK

  • Your mobile application needs to have been granted a location permission. See the platform specific section of this document for more information.

  • The phone number must have an international prefix and respect this regex format: ^[+][1-9][0-9]{6,14}$

stopProtection()#

Stop a protection session that was previously started with startProtection(). This method has no effect if status.isProtectionActive is false.

stopAlertCountdown()#

Stop an alert countdown that started during an ongoing protection session. This method has no effect if status.alert is null.

cancelEmergencyRequest()#

Cancel an emergency that is being sent or has already been sent.This method has no effect if status.emergency is null.

resumeProtectionAfterEmergency()#

After an emergency was sent, resume sensor processing to detect new accidents. This method has no effect if status.emergency is null or status.emergency.isEmergencySent is false or status.emergency.hasEmergencySendingFailed is false.

reportProblem()#

If you notice a problem during a protection session, you can report a problem. This will upload debug information to SafeRider servers.

iOS#

Using the LRSaferiderDelegationProtocol :

self.safeRiderInstance.problemReportDelegate = self

extension MyClass: LRProblemReportDelegate {

    func onProblemReportSuccess() {
        log.info("succeed to report problem")
    }

    func onProblemReportFailure(_ error: LRProtectionError) {
        log.error("fail to report problem: \(error.localizedDescription)")
    }
}

Using the LRSaferiderReactiveProtocol :

var cancellableBag = Set<AnyCancellable>()

self.safeRiderInstance.reportProblem()
    .sink { completion in
        switch completion {
        case .failure(let error):
            log.error("fail to report problem: \(error)")
        default: break
        }
    } receiveValue: { _ in
        log.debug("succeed to report problem")
    }
    .store(in: &self.cancellableBag)

setFallSurveyAnswer(hasFallen)#

Set the fall survey user answer if you have implemented your own accident workflow UI.

Debugging#

These methods allow you to debug or test your SDK integration from a developper-only menu.

triggerFakeAlertCountdown(location)#

Start a fake alert countdown, short-circuiting the accident detection algorithms.

Location parameter type is:

  • on Android, android.location.Location

  • on iOS, CoreLocation.CLLocation

sendFakeEmergencyRequest(location)#

Send a fake emergency to SafeRider servers, bypassing the accident detection algorithms and the alert countdown.

Location parameter type is:

  • on Android, android.location.Location

  • on iOS, CoreLocation.CLLocation

simulateFakeShock(date)#

Simulate a fake shock in the accident detection algorithm. This method is for advanced use cases and might be removed in the future.

Date parameter type is:

  • on Android, java.lang.Long (Date in milliseconds)

  • on iOS, Date

Errors#

Android#

LRStartProtectionResult#

Result of the startProtection() command. It indicates if the protection has been activated successfully or not. In case protection can’t be started the corresponding error is returned. Errors can also be checked via getConfigurationErrors() method on the SafeRider instance.

Fields:

  • isActive : Boolean

  • error : LRProtectionError?

LRProtectionError#

Class which represents protection errors. It contains a field (isBlockingError) to indicate if the error must be resolved in order to start protection.

EmptyUserId#

Blocking

This error can be returned from startProtection(). An userId must be provided in order to start protection.

InvalidPhoneNumber#

Blocking

This error can be returned from startProtection(). A valid phone number must be provided in order to start protection.

LocationPermissionNotGranted#

Blocking

This error can be returned from startProtection() or getConfigurationErrors(). Location permission must be granted to start protection.

BackgroundLocationPermissionNotGranted#

Blocking

This error can be returned from startProtection() or getConfigurationErrors(). Background Location permission must be granted to start protection.

GpsDisabled#

Blocking

This error can be returned from startProtection() or getConfigurationErrors(). Gps must be enabled to start protection.

LocationDisabled#

Blocking

This error can be returned from startProtection() or getConfigurationErrors(). Location must be enabled to start protection.

BatterySavingEnabled#

Non blocking

This error can be returned from getConfigurationErrors(). Any battery saving feature should be disabled in order to have reliable protection.

BackgroundProcessingNotGranted#

Non blocking

This error can be returned from getConfigurationErrors(). Background processing should be allowed in order to prevent applications from being killed in background.

iOS#

enum LRSafeRiderError: Int, Error {

    /// 10xxx: iOS problems
    case iOSLocationServicesDisabled = 10000
    case iOSLocationAuthorizationNotGranted = 10001
    case iOSMotionActivityAuthorizationNotGranted = 10002

    /// 12xxx: sdk problems
    case invalidApiKey = 12000
    case notInitialized = 12001
    case invalidPhoneNumber = 12002
    case emptyUserId = 12003

    /// 13xxx: operation calls
    case operationFailed = 13000
    case operationInvalidCall = 13001
    public var localizedDescription: String {
        switch self {
        case .iOSLocationServicesDisabled:
            return "You should enable location services"
        case .iOSLocationAuthorizationNotGranted:
            return "You should request and grant Location authorization (at least 'when in use' before start protection)"
        case .iOSMotionActivityAuthorizationNotGranted:
            return "You should request and grant Motion & Fitness authorization"
        case .invalidApiKey:
            return "You have to provide a valid api key"
        case .notInitialized:
            return "You have to call initialize(apiKey: String) before calling any function"
        case .invalidPhoneNumber:
            return "You should provide a valid phone number (ex: +33600000000 for France)"
        case .emptyUserId:
            return "You should provide a not empty user id"
        case .operationFailed:
            return "The operation has failed"
        case .operationInvalidCall:
            return "The operation can not be called considering current protection status"
        }
    }