=================== Developer Reference =================== `Android API reference `_ Protection Status Updates ========================= The SDK exposes a data structure named :code:`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 :code:`LRProtectionStatus` structure are as follows. isProtectionActive: Boolean ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Whether a protection session is running. vehicleType: LRVehicleType? ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Available only when :code:`isProtectionActive` is true. This reflects the parameter that was passed to :code:`startProtection()`. Values: * on Android, :code:`MOTORCYCLE`, :code:`BICYCLE` or :code:`CAR` * on iOS, :code:`.motorcycle`, :code:`.bicycle` or :code:`.car` sessionId: String? ~~~~~~~~~~~~~~~~~~ Available only when :code:`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 :code:`Long` * on iOS, this is a :code:`Date` alertDuration """"""""""""" * on Android, this is a :code:`Long` * on iOS, this is a :code:`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 :ref:`US#5 - Quick Survey to cancel the Emergency `). The SDK exposes a data structure named :code:`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: .. code-block:: kotlin 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 :code:`LRSaferiderDelegationProtocol` : .. code-block:: swift self.safeRiderInstance.protectionDelegate = self extension MyClass: LRProtectionDelegate { func onFallSurveyAnswer(_ fallSurveyAnswer: LRFallSurveyAnswer?) { log.debug("user has fallen: \(fallSurveyAnswer.hasFallen)") } } Using the :code:`LRSaferiderReactiveProtocol` : .. code-block:: swift var cancellableBag = Set() self.safeRiderInstance.fallSurveyAnswerPublisher .sink { fallSurveyAnswer in log.debug("user has fallen: \(fallSurveyAnswer.hasFallen)") } .store(in: &self.cancellableBag) SDK Methods =========== initialize() ~~~~~~~~~~~~ Android """"""" .. code-block:: kotlin initialize(context: Context, apiKey: String, notification: LRNotification? = null, tripDetectionNotification: LRNotification? = null, showAccidentUi: Boolean = true, accidentUiTextToSpeech: String? = null, accidentUiScreenLogo: Int? = nil) iOS """ .. code-block:: swift 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 :code:`onCreate()` method (Android) or AppDelegate :code:`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: :code:`UninitializedPropertyAccessException` * on iOS: :code:`LRProtectionError.invalidApiKey` Set :code:`showAccidentUi` to false to disable the included accident screens. In this case you need to implement the screens yourself following the specifications in :doc:`Typical App User Stories `. If :code:`showAccidentUi` is set to true, you could: * set :code:`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 :code:`accidentUiScreenLogo`: it will be displayed at the top of all the accident workflow's screens * (on iOS only) set your dark mode rectangular logo :code:`accidentUiScreenLogoDarkMode` too if your app manages the dark mode and your :code:`accidentUiScreenLogo` is not optimized for it. NB: if you do not set at least :code:`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 :ref:`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 """"""" .. code-block:: kotlin startProtection(vehicleType: LRVehicleType, userId: String, phoneNumber: String, firstName: String?, lastName: String?, metadata: Map?) : LRStartProtectionResult iOS """ Using the :code:`LRSafeRiderDelegationProtocol` : .. code-block:: swift func startProtection(vehicleType: LRVehicleType, userId: String, phoneNumber: String, userFirstName: String?, userLastName: String?, metadata: [String : Any]?) Using the :code:`LRSafeRiderReactiveProtocol` : .. code-block:: swift func startProtection(vehicleType: LRVehicleType, userId: String, phoneNumber: String, userFirstName: String?, userLastName: String?, metadata: [String : Any]?) -> AnyPublisher 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 :ref:`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: :code:`^[+][1-9][0-9]{6,14}$` stopProtection() ~~~~~~~~~~~~~~~~ Stop a protection session that was previously started with :code:`startProtection()`. This method has no effect if :code:`status.isProtectionActive` is false. stopAlertCountdown() ~~~~~~~~~~~~~~~~~~~~ Stop an alert countdown that started during an ongoing protection session. This method has no effect if :code:`status.alert` is null. cancelEmergencyRequest() ~~~~~~~~~~~~~~~~~~~~~~~~ Cancel an emergency that is being sent or has already been sent.This method has no effect if :code:`status.emergency` is null. resumeProtectionAfterEmergency() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After an emergency was sent, resume sensor processing to detect new accidents. This method has no effect if :code:`status.emergency` is null or :code:`status.emergency.isEmergencySent` is false or :code:`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 :code:`LRSaferiderDelegationProtocol` : .. code-block:: swift 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 :code:`LRSaferiderReactiveProtocol` : .. code-block:: swift var cancellableBag = Set() 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, :code:`android.location.Location` * on iOS, :code:`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, :code:`android.location.Location` * on iOS, :code:`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, :code:`java.lang.Long` (Date in milliseconds) * on iOS, :code:`Date` Errors ====== Android ~~~~~~~ LRStartProtectionResult """"""""""""""""""""""" Result of the :code:`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 :code:`getConfigurationErrors()` method on the SafeRider instance. Fields: * :code:`isActive` : Boolean * :code:`error` : :code:`LRProtectionError?` LRProtectionError """"""""""""""""" Class which represents protection errors. It contains a field (:code:`isBlockingError`) to indicate if the error must be resolved in order to start protection. EmptyUserId """"""""""" **Blocking** This error can be returned from :code:`startProtection()`. An :code:`userId` must be provided in order to start protection. InvalidPhoneNumber """""""""""""""""" **Blocking** This error can be returned from :code:`startProtection()`. A valid phone number must be provided in order to start protection. LocationPermissionNotGranted """""""""""""""""""""""""""" **Blocking** This error can be returned from :code:`startProtection()` or :code:`getConfigurationErrors()`. Location permission must be granted to start protection. BackgroundLocationPermissionNotGranted """""""""""""""""""""""""""""""""""""" **Blocking** This error can be returned from :code:`startProtection()` or :code:`getConfigurationErrors()`. Background Location permission must be granted to start protection. GpsDisabled """"""""""" **Blocking** This error can be returned from :code:`startProtection()` or :code:`getConfigurationErrors()`. Gps **must be enabled** to start protection. LocationDisabled """""""""""""""" **Blocking** This error can be returned from :code:`startProtection()` or :code:`getConfigurationErrors()`. Location must be enabled to start protection. BatterySavingEnabled """""""""""""""""""" **Non blocking** This error can be returned from :code:`getConfigurationErrors()`. Any battery saving feature should be disabled in order to have reliable protection. BackgroundProcessingNotGranted """""""""""""""""""""""""""""" **Non blocking** This error can be returned from :code:`getConfigurationErrors()`. Background processing should be allowed in order to prevent applications from being killed in background. iOS ~~~ .. code-block:: swift 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" } }