LogoLogo
SentianceInsights-Control-TowerLegacy DocsImportant Security Notice
  • Introduction
  • Integrating the SDK
    • Getting Started
    • Android Quick Start
      • 1. Including the SDK
      • 2. Initialization
      • 3. User Creation
      • 4. Enabling Detections
      • 5. SDK Status Updates
      • 6. Permissions
      • 7. Additional Requirements
    • iOS Quick Start
      • 1. Including the SDK
        • Using CocoaPods
        • Using Carthage
        • Using Swift Package Manager
        • Manual Integration
      • 2. Project Settings
      • 3. Initialization
      • 4. User Creation
      • 5. Enabling Detections
      • 6. SDK Status Updates
      • 7. Permissions
      • 8. Additional Requirements
    • React Native Quick Start
      • 1. Including the SDK
      • 2. Configuration
      • 3. Initialization
      • 4. User Creation
      • 5. Enabling Detections
      • 6. SDK Status Updates
    • Flutter Quick Start
      • 1. Including the SDK
      • 2. Configuration
      • 3. Initialization
      • 4. User Creation
      • 5. Enabling Detections
      • 6. SDK Status Updates
    • What Comes After Integration
    • Integration (FAQ)
    • Detections (FAQ)
  • Sentiance Insights
    • Overview of Sentiance Insights
      • Driving Insights
        • Transports
        • Driving Events and Scores
        • Car Crashes
      • Mobility and Lifestyle Insights
        • Visit Essentials
        • User Segmentation
    • Engagement
      • User Adaptive Score
      • Streaks
      • Challenges
      • Reward System
      • Communication Campaign
      • Social Groups
        • Technical Details
    • Accessing Sentiance Insights
      • via On Device API
      • via Cloud API
      • via Firehose - Realtime Messages
      • via Offloads
  • FAQ
    • Security, Privacy and Terms of Service (FAQ)
  • Important topics
    • SDK
      • Appendix
        • Android
          • Android 10 Update Behavior
          • Android Battery Optimization
          • Artifacts & Dependencies
          • Manifest Permissions
          • Notification Management
          • Sample Notification
          • Supported API Levels
        • iOS
          • App Store Privacy Section
          • App Store Release
          • Apple Privacy Manifest
          • ARM Simulator Support
          • Dependencies
          • iOS 13 permission changes
          • Supported iOS Versions & Architectures
          • Swift Support
          • v5.x Framework Files
          • v6.x Framework Files
        • Controlled Detections
          • Automatic Detections
          • Automatic Detections with Forced Trips
          • Controlled Trips Only
          • Checking Trip Status
        • Control Sending Data
        • Custom User Metadata
        • Detecting Vehicle Crashes
        • Feature Production Readiness
        • Migration Guide
          • Android
          • iOS
          • React Native
          • Flutter
        • On-Device Features
        • React Native
        • SDK Initialization
        • User Access Token
        • User Creation
        • User Linking
        • Xamarin
      • API Reference
        • Android
          • AsyncInitializationError
          • AsyncInitializationResult
          • DateTime
          • DetectionStatus
          • DisableDetectionsError
          • DisableDetectionsResult
          • Driving Insights
            • CallWhileMovingEvent
            • DrivingInsight
            • DrivingInsightsApi
            • DrivingInsightsReadyListener
            • HarshDrivingEvent
              • Type
            • PhoneUsageEvent
            • SafetyScores
            • SafetyScoreRequestParameters
              • Builder
              • OccupantRoles
              • Period
              • TransportModes
            • SpeedingEvent
          • Smart Geofences
            • DetectionMode
            • SmartGeofence
            • SmartGeofenceApi
            • SmartGeofenceEvent
              • Type
            • SmartGeofenceEventListener
            • SmartGeofencesRefreshError
              • SmartGeofencesRefreshFailureReason
            • SmartGeofencesRefreshResult
          • EnableDetectionsError
            • EnableDetectionsFailureReason
          • EnableDetectionsResult
          • EventTimeline
            • EventType
            • TransportMode
            • Event
              • OffTheGridEvent
              • StationaryEvent
              • TransportEvent
              • UnknownEvent
            • EventTimelineUpdateListener
            • EventTimelineApi
            • OccupantRole
            • Venue
              • VenueType
              • VenueSignificance
          • Feedback
            • FeedbackApi
            • OccupantRoleFeedback
            • OccupantRoleFeedbackResult
          • GeoLocation
          • InitializationResult
            • InitializationFailureReason
          • InitState
          • MetaUserLinker
          • MetaUserLinkerAsync
          • MetaUserLinkerCallback
          • OnInitCallback
            • InitIssue
          • OnSdkStatusUpdateHandler
          • OnStartFinishedHandler
          • PendingOperation
            • OnSuccessListener
            • OnFailureListener
            • OnFailureListener
            • OnCompleteListener
          • ResetCallback
            • ResetCallback.ResetFailureReason
          • ResetError
            • ResetFailureReason
          • ResetResult
          • SdkConfig
            • LocationPermission
            • Builder
          • SdkException
          • SdkStatus
            • LocationSetting
            • Quota Status
            • StartStatus
          • SdkStatusUpdateListener
          • Sentiance
          • SentianceOptions
            • SentianceOptions.Builder
          • StartTripError
            • StartTripFailureReason
          • StartTripResult
          • StationaryInfo
          • StopTripError
            • StopTripFailureReason
          • StopTripResult
          • SubmitDetectionsCallback
          • SubmitDetectionsError
            • SubmitDetectionsFailureReason
          • SubmitDetectionsResult
          • Token
          • TokenResultCallback
          • TransmittableDataType
          • Transport Sessions
            • SensorDataChunk
            • TransportSession
            • TransportSessionApi
            • TransportSessionListener
          • Trip
            • StartTripCallback
            • StopTripCallback
            • TransportMode
            • TripTimeoutListener
            • TripType
          • TripInfo
          • UserAccessTokenError
            • UserAccessTokenFailureReason
          • UserActivity
          • UserActivityListener
          • UserActivityType
          • UserContext
            • Attribute
            • Segment
              • SegmentCategory
              • SegmentSubcategory
              • SegmentType
            • SemanticTime
          • UserContextApi
            • RequestUserContextFailureReason
            • RequestUserContextError
            • UserContextUpdateCriteria
            • UserContextUpdateListener
          • UserCreationError
            • UserCreationFailureReason
          • UserCreationOptions
            • UserCreationOptions.Builder
          • UserCreationResult
            • UserInfo
          • UserLinker
          • UserLinkerAsync
          • UserLinkerCallback
          • UserLinkingError
            • UserLinkingFailureReason
          • UserLinkingResult
          • VehicleCrashDetection
            • CrashDetectionApi
            • CrashDetectionState
            • VehicleCrashDiagnostic
            • VehicleCrashEvent
            • VehicleCrashDiagnosticListener
            • VehicleCrashListener
            • VehicleCrashSeverity
          • Waypoint
        • iOS
          • Detections
            • Enable Detections
            • Disable Detections
          • Driving Insights
            • SENTCallWhileMovingEvent
            • SENTDrivingInsights
            • SENTDrivingInsightsReadyDelegate
            • SENTHarshDrivingEvent
              • HarshDrivingEventType
            • SENTPhoneUsageEvent
            • SENTSpeedingEvent
            • SENTSafetyScores
            • SafetyScoreRequestParameters
              • SafetyScoreRequestOccupantRoleOption
              • SafetyScoreRequestPeriod
              • SafetyScoreRequestTransportModeOption
          • Event Timeline
            • EventTimelineDelegate
            • SENTOccupantRole
            • SENTTimelineEvent
              • SENTOffTheGridEvent
              • SENTStationaryEvent
              • SENTTransportEvent
              • SENTUnknownEvent
            • SENTTimelineEventType
            • SENTTimelineTransportMode
            • SENTVenue
            • SENTVenueSignificance
            • SENTVenueType
          • Feedback
            • SENTFeedback
            • SENTOccupantRoleFeedback
            • SENTOccupantRoleFeedbackResult
          • SENTCompletionHandlers
          • SENTConfig
          • SENTDate
          • SENTGeolocation
          • SENTAsyncInitializationCompletionHandler
          • SENTAsyncInitializationError
          • SENTAsyncInitializationResult
          • Sentiance
          • SENTInitializationResult
          • SENTOptions
            • SENTOptionsInitPurpose
          • SENTPublicDefinitions
          • SENTSDKStatus
          • SENTToken
          • SENTTransmittableDataType
          • SENTUserActivity
          • SENTVehicleCrashDetectionState
          • SENTVehicleCrashDiagnostic
          • SENTVehicleCrashEvent
            • SENTVehicleCrashSeverity
          • SENTWaypoint
          • Smart Geofences
            • SENTSmartGeofencesRefreshHandler
            • SmartGeofenceDetectionMode
            • SmartGeofencesRefreshError
              • SmartGeofencesRefreshFailureReason
            • SmartGeofencesRefreshResult
            • SmartGeofence
            • SmartGeofenceEvent
              • SmartGeofenceEventType
            • SmartGeofenceEventDelegate
          • Transport Sessions
            • SENTAccelerometerDataIterator
            • SENTLocationDataIterator
            • SENTSensorDataChunk
            • SENTTransportSession
          • User Context
            • SENTAttribute
            • SENTRequestUserContextCompletionHandler
            • SENTRequestUserContextError
            • SENTRequestUserContextFailureReason
            • SENTSegment
              • SENTSegmentCategory
              • SENTSegmentSubCategory
              • SENTSegmentType
            • SENTSemanticTime
            • SENTUserContext
            • SENTUserContextDelegate
            • SENTUserContextUpdateCriteria
          • User Creation & Linking
            • SENTUserInfo
            • User Creation
              • SENTUserCreationResult
              • SENTUserCreationFailureReason
              • SENTUserCreationError
              • SENTUserCreationCompletionHandler
              • SENTUserCreationOptions
            • User Linking
              • SENTNoOpUserLinker
              • SENTUserLinkingResult
              • SENTUserLinkingFailureReason
              • SENTUserLinkingError
              • SENTUserLinkingCompletionHandler
              • SENTUserLinker
        • React Native
          • core
            • Definitions
            • Examples
          • crash-detection
            • Definitions
            • Examples
          • driving-insights
            • Definitions
            • Examples
          • smart-geofences
            • Definitions
            • Examples
          • event-timeline
            • Definitions
            • Examples
          • legacy
            • Definitions
            • Examples
          • user-context
            • Definitions
            • Examples
        • Flutter
      • Battery Optimization on Android
      • How To
        • Check the Location Permissions
        • Utilize the Event Timeline API
        • Utilize the Driving Insights API
        • Utilize the User Context API
        • Utilize the Smart Geofences API
      • Troubleshooting
        • Android
        • iOS
      • Changelog
        • Android
        • iOS
        • React Native
        • Flutter
    • Authentication and Authorization
    • User Creation (via Authentication Code)
    • User Deletion
    • PlayStore Location Access Review
    • Privacy Report & Dashboard
    • Crash Detection
    • Important Security Notice
    • Injecting Fake Data
    • Custom Aggregation For Analytics
    • Merging Scores for Motorcycles and Cars
  • Links
    • SDK Standard License
    • Insights-by-Sentiance Application
    • Insights-by-Sentiance Privacy Policy
    • Journeys License
    • Technical & Organizational Measures
Powered by GitBook
On this page
  • Query for Historic Events
  • Subscribe for Event Timeline Updates
  1. Important topics
  2. SDK
  3. How To

Utilize the Event Timeline API

PreviousCheck the Location PermissionsNextUtilize the Driving Insights API

Last updated 1 month ago

Accessing some of the API classes that are mentioned on this page requires additional Sentiance SDK dependencies. See for more information.

On this page, you can find examples of how to query the Sentiance SDK for historic timeline events, and how to register to receive timeline updates in your app, as new events are detected or existing ones are updated.

Query for Historic Events

let from = Date.distantPast
let to = Date.distantFuture

Sentiance.shared.getTimelineEvents(from: from, to: to).forEach { event in
    print("Event ID: \(event.eventId)")
    print("Started on: \(event.startDate)")
    print("Ended on: \(event.endDate)")
    
    if event.type == .inTransport {
        let transport =  event as! SENTTransportEvent
        
        print("Type: transport")
        print("Mode: \(transport.transportMode)")
        
        if let distance = transport.distanceInMeters {
            print("Distance: \(distance)")
        }
        
        print("Waypoints: \(transport.waypoints)")
        
    }
    else if event.type == .stationary {
        let stationary =  event as! SENTStationaryEvent
        
        print("Type: stationary")
        print("Location: \(stationary.location)")
        print("Venue: \(stationary.venue)")
    }
    else if event.type == .offTheGrid {
        print("Type: off-the-grid")
    }
    else {
        print("Type: unknown")
    }
}
val from = Date(0)
val to = Date(Long.MAX_VALUE)

EventTimelineApi.getInstance(context).getTimelineEvents(from, to).forEach { event ->
    print("Event ID: ${event.id}")
    print("Started on: ${event.startTime}")
    print("Ended on: ${event.endTime}")

    when (event.eventType) {
        EventType.IN_TRANSPORT -> {
            val transport = event as TransportEvent!

            print("Type: transport")
            print("Mode: ${transport.transportMode}")

            if (transport.distanceInMeters != null) {
                print("Distance: ${transport.distanceInMeters}")
            }

            print("Waypoints: ${transport.waypoints}")
        }
        EventType.STATIONARY -> {
            val stationary = event as StationaryEvent!

            print("Type: stationary")
            print("Location: ${stationary.location}")
            print("Venue: ${stationary.venue}")
        }
        EventType.OFF_THE_GRID -> {
            print("Type: off-the-grid")
        }
        else -> {
            print("Type: unknown")
        }
    }
}
import EventTimelineApi from '@sentiance-react-native/event-timeline';

const from = new Date(0); // 01/01/1970
const to = new Date(2 ** 31 * 1000);

const events = await EventTimelineApi.getTimelineEvents(from.getTime(), to.getTime());
for (const event of events) {
    console.log(`Event ID: ${event.id}`);
    console.log(`Started on: ${event.startTime}`);
    console.log(`Ended on: ${event.endTime}`);

    switch (event.type) {
        case 'IN_TRANSPORT':
            const transport = event;
            console.log('Type: transport');
            console.log(`Mode: ${transport.transportMode}`);

            if (transport.distance) {
                console.log(`Distance: ${transport.distance}`);
            }
            console.log(`Waypoints: ${JSON.stringify(transport.waypoints)}`);
            break;
        case 'STATIONARY':
            const stationary = event;
            console.log('Type: stationary');
            console.log(`Location: ${JSON.stringify(stationary.location)}`);
            console.log(`Venue: ${JSON.stringify(stationary.venue)}`);
            break;
        case 'OFF_THE_GRID':
            console.log('Type: off-the-grid');
            break;
        default:
            console.log('Type: unknown');
    }
}
    
app.dart
import 'package:sentiance_event_timeline/sentiance_event_timeline.dart';

final eventTimeline = SentianceEventTimeline();

/// A function that processes all events that occurred since the dawn of times.
void processTimelineEvents() async {
  const startEpochTimeMs = 0;
  final endEpochTimeMs = DateTime.now().millisecondsSinceEpoch;
  final timelineEvents = await eventTimeline.getTimelineEvents(startEpochTimeMs, endEpochTimeMs);

  for (final event in timelineEvents) {
    if (event is StationaryEvent) {
      print("Type: stationary");
      print("Location: ${event.location}");
      print("Venue: ${event.venue}");
    } else if (event is TransportEvent) {
      print("Type: transport");
      print("Mode: ${event.transportMode}");
      print("Distance: ${event.distance}");
      for (final waypoint in event.waypoints) {
        print(waypoint.toString());
      }
    } else if (event is OffTheGridEvent) {
      print("Type: off-the-grid");
    } else if (event is UnknownEvent) {
      print("Type: unknown");
    }
  }
}

Subscribe for Event Timeline Updates

public class EventTimelineUpdateReceiver: EventTimelineDelegate {
    private let LAST_UPDATE_KEY = "SentianceTimelineEventLastUpdateTime"
    private let timelineStore: TimelineStore
    private let queue: OperationQueue
    
    init() {
        timelineStore = TimelineStore() // your own store implementation
        queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
    }    
        
    public func listenForUpdates() {
        // Save a local copy of the most recent event update time, before 
        // setting the delegate below. This way, we make sure that this 
        // value does not change by the delegate method, by the time we query
        // for updates.
        let mostRecentEventUpdateTime = self.mostRecentEventUpdateTime
                
        // Set the delegate to start listening for timeline updates.
        Sentiance.shared.eventTimelineDelegate = self
        
        // Finally, let's make sure we haven't missed any events since the 
        // last time our app ran (e.g. due to unexpected app termination).
        let afterDate = Date(timeIntervalSince1970: mostRecentEventUpdateTime)
        Sentiance.shared.getTimelineUpdates(after: afterDate)
          .forEach { [weak self] timelineEvent in
            self?.updateOrInsert(event: timelineEvent, 
                                 updateMostRecentEventUpdateTime: false)
        }
    }
    
    public func onEventTimelineUpdate(event: SENTTimelineEvent) {
        // Note: this delegate method invocation happens on the 
        // main/UI thread.
        updateOrInsert(event: event, updateMostRecentEventUpdateTime: true)
    }
    
    private func updateOrInsert(event: SENTTimelineEvent,
                                updateMostRecentEventUpdateTime: Bool) {
        // The non-concurrent queue makes sure we don't process incoming 
        // updates via the delegate method and the result of calling
        // getTimelineUpdates() at the same time. Plus, it makes sure
        // we do the processing off of the main thread.
        queue.addOperation { [weak self] in
            guard let self = self else { return }
            
            if let existingEvent = timelineStore.get(id: event.eventId) {
                // Make sure the event we're about to update isn't older 
                // than the one we already have.
                if (existingEvent.lastUpdateDate.timeIntervalSince1970 < 
                     event.lastUpdateDate.timeIntervalSince1970) {
                    timelineStore.update(event: event)
                }
            } else {
                timelineStore.insert(event: event)
            }
            
            if updateMostRecentEventUpdateTime {
                // Note: events arriving via the onEventTimelineUpdate 
                // delegate method have a monotonically increasing 
                // lastUpdateDate.
                self.mostRecentEventUpdateTime 
                   = event.lastUpdateDate.timeIntervalSince1970
            }
        }
    }
    
    // We keep track of the most recent lastUpdateDate of the received
    // events, so that on the next run, we can use it to process missed
    // events (see listenForUpdates()).
    private var mostRecentEventUpdateTime : TimeInterval {
        get {
            // We use UserDefaults. But if your app uses the iOS
            // DataProtection capability, this won't work for you
            // when the device is locked. Use something that will
            // be accessible in this case, such as the keychain or
            // a file with a specific protection option.
            let defaults = UserDefaults.standard
            var updateTime = defaults.double(forKey: LAST_UPDATE_KEY)
            if updateTime == 0 {
                updateTime = Date().timeIntervalSince1970
                self.mostRecentEventUpdateTime = updateTime
            }
            return updateTime
        }
        set {
            let defaults = UserDefaults.standard
            defaults.set(newValue, forKey: LAST_UPDATE_KEY)
        }
    }
}
public class EventTimelineUpdateReceiver(val context: Context) {
    private val timelineStore: TimelineStore
    private val executor: ExecutorService

    init {
        timelineStore = TimelineStore() // your own store implementation
        executor = Executors.newSingleThreadExecutor()
    }

    fun listenForUpdates() {
        val api = EventTimelineApi.getInstance(context)

        // Save a local copy of the most recent event time, before setting 
        // the listener below. This way, we make sure that this value does not
        // change by the listener callback method, by the time we query for
        // updates.
        val mostRecentEventUpdateTime = getMostRecentEventUpdateTime()

        // Set the listener to start listening for timeline updates.
        api.setTimelineUpdateListener { event ->
            // Note: this invocation happens on the main/UI thread.
            updateOrInsert(event, updateMostRecentEventUpdateTime = true)
        }

        // Finally, let's make sure we haven't missed any events since the
        // last time our app ran (e.g. due to unexpected app termination).
        api.getTimelineUpdates(Date(mostRecentEventUpdateTime)).forEach { event ->
            updateOrInsert(event, updateMostRecentEventUpdateTime = false)
        }
    }

    private fun updateOrInsert(event: Event, updateMostRecentEventUpdateTime: Boolean) {
        // The single thread executor makes sure we don't process incoming
        // updates via the listener callback method and the result of calling
        // getTimelineUpdates() at the same time. Plus, it makes sure we do
        // the processing off of the main thread.
        executor.submit {
            val existingEvent = timelineStore.get(event.id)
            if (existingEvent != null) {
                // Make sure the event we're about to update isn't older
                // than the one we already have.
                if (existingEvent.lastUpdateTime < event.lastUpdateTime) {
                    timelineStore.update(event)
                }
            } else {
                timelineStore.insert(event)
            }

            if (updateMostRecentEventUpdateTime) {
                // Note: events arriving listener callback method have
                // monotonically increasing lastUpdateTime.
                setMostRecentEventUpdateTime(event.lastUpdateTime.epochTime)
            }
        }
    }


    // We keep track of the most recent lastUpdateTime of the received
    // events, so that on the next run, we can use it to process missed
    // events (see listenForUpdates()).
    private fun getMostRecentEventUpdateTime(): Long {
        val prefs = context.getSharedPreferences(null, Context.MODE_PRIVATE)
        var updateTime = prefs.getLong(EventTimelineUpdateReceiver.PREFS_KEY_LAST_UPDATE_TIME, -1)
        if (updateTime < 0) {
            // Store the current time, so that in the future, we start from here.
            updateTime = System.currentTimeMillis()
            setMostRecentEventUpdateTime(updateTime)
        }
        return updateTime
    }
    
    private fun setMostRecentEventUpdateTime(timestamp: Long) {
        val prefs = context.getSharedPreferences(null, Context.MODE_PRIVATE)
        prefs.edit().putLong(EventTimelineUpdateReceiver.PREFS_KEY_LAST_UPDATE_TIME, timestamp).apply()
    }

    companion object {
        const val PREFS_KEY_LAST_UPDATE_TIME = "SentianceTimelineEventLastUpdateTime"
    }
}
// We're using the DefaultPreference library to store key/value pairs (using 
// SharedPreferences on Android, and UserDefaults on iOS). Feel free to use 
// a library of your own choosing.
// Note however that if your app uses the iOS DataProtection capability, 
// accessing UserDefaults won't work when the device is locked. In this 
// case, use something that will be accessible (such as the keychain or
// a file with a specific protection option).

import DefaultPreference from 'react-native-default-preference';
import EventTimelineApi from '@sentiance-react-native/event-timeline';

const KEY_LAST_UPDATE_TIME = 'SentianceTimelineEventLastUpdateTime';
const timelineStore = getTimelineStore(); // your own store implementation

listenForUpdates(); // Start listening to event updates

async function listenForUpdates() {
    // Save a local copy of the most recent event update time, before setting
    // the listener below. This way, we make sure that this value does not
    // change by the listener callback method, by the time we query for updates.
    const mostRecentEventUpdateTime = await getMostRecentEventUpdateTime();

    // Set the listener to start listening for timeline updates.
    const subscription = await EventTimelineApi.addTimelineUpdateListener(
        async event => await updateOrInsert(event, true));

    // Finally, let's make sure we haven't missed any events since the
    // last time our app ran (e.g. due to unexpected app termination).
    const events = await EventTimelineApi.getTimelineUpdates(mostRecentEventUpdateTime);
    for (let event in events) {
        await updateOrInsert(event, false);
    }
}

async function updateOrInsert(event, updateMostRecentEventUpdateTime) {
    // Make sure to handle concurrent access appropriately here
    
    const existingEvent = await timelineStore.get(event.id);
    if (existingEvent) {
        // Make sure the event we're about to update isn't older
        // than the one we already have.
        if (existingEvent.lastUpdateTimeEpoch < event.lastUpdateTimeEpoch) {
            await timelineStore.update(event);
        }
    } else {
        await timelineStore.insert(event);
    }

    if (updateMostRecentEventUpdateTime) {
        // Note: events arriving through the listener callback method have
        // a monotonically increasing lastUpdateTimeEpoch.
        await setMostRecentEventUpdateTime(event.lastUpdateTimeEpoch)
    }
}

async function getMostRecentEventUpdateTime() {
    let updateTimeEpoch = await DefaultPreference.get(KEY_LAST_UPDATE_TIME);
    if (updateTimeEpoch < 0) {
        // Store the current time, so that in the future, we start from here.
        updateTimeEpoch = Date.now();
        await setMostRecentEventUpdateTime(updateTimeEpoch);
    }
    return updateTimeEpoch;
}

async function setMostRecentEventUpdateTime(timestamp) {
    await DefaultPreference.set(KEY_LAST_UPDATE_TIME, timestamp);
}

Create a background.dart file under your project's lib folder with the following code:

background.dart
// We're using the shared_preferences Flutter package to store key/value pairs 
// (using SharedPreferences on Android, and UserDefaults on iOS). 
// Feel free to use a library of your own choosing.
// Note however that if your app uses the iOS DataProtection capability, 
// accessing UserDefaults won't work when the device is locked. In this 
// case, use something that will be accessible (such as the keychain or
// a file with a specific protection option).
import 'package:flutter/material.dart';
import 'package:sentiance_event_timeline/sentiance_event_timeline.dart';
import 'package:shared_preferences/shared_preferences.dart';

const keyLastUpdateTime = 'SentianceTimelineEventLastUpdateTime';
final sentianceEventTimeline = SentianceEventTimeline();
const timelineStore = getTimelineStore(); // your own store implementation

@pragma('vm:entry-point')
void registerEventTimelineListener() async {
  WidgetsFlutterBinding.ensureInitialized();
  SharedPreferences sharedPrefs = await SharedPreferences.getInstance();

  // Save a local copy of the most recent event update time, before setting
  // the listener below. This way, we make sure that this value does not
  // change by the listener callback method, by the time we query for updates.
  final mostRecentEventUpdateTime = await getMostRecentEventUpdateTime();

  // Set the listener to start listening for timeline updates.
  SentianceEventTimeline.registerEventTimelineUpdateListener((timelineEvent) {
    await updateOrInsert(timelineEvent, true);
  });

  // Finally, let's make sure we haven't missed any events since the
  // last time our app ran (e.g. due to unexpected app termination).
  final events = await sentianceEventTimeline.getTimelineUpdates(mostRecentEventUpdateTime);
  for (var event in events.nonNulls) {
    await updateOrInsert(event, false);
  }
}

Future<void> updateOrInsert(TimelineEvent event, bool updateMostRecentEventUpdateTime) async {
  final existingEvent = await timelineStore.get(event.id); // your own store implementation
  if (existingEvent) {
    // Make sure the event we're about to update isn't older
    // than the one we already have.
    if (existingEvent.lastUpdateTimeMs < event.lastUpdateTimeMs) {
      await timelineStore.update(event);
    }
  } else {
    await timelineStore.insert(event);
  }

  if (updateMostRecentEventUpdateTime) {
    // Note: events arriving through the listener callback method have
    // a monotonically increasing lastUpdateTimeMs.
    await setMostRecentEventUpdateTime(event.lastUpdateTimeMs);
  }
}

Future<void> setMostRecentEventUpdateTime(int timestamp) async {
  SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
  await sharedPrefs.setInt(keyLastUpdateTime, timestamp);
}

Future<int> getMostRecentEventUpdateTime() async {
  SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
  var updateTimeEpoch = sharedPrefs.getInt(keyLastUpdateTime);
  if (updateTimeEpoch == null) {
    // Store the current time, so that in the future, we start from here.
    updateTimeEpoch = DateTime.now().millisecondsSinceEpoch;
    await setMostRecentEventUpdateTime(updateTimeEpoch);
  }

  return updateTimeEpoch;
}

Add the following code, depending on your target platform.

For iOS, add the following to your app delegate class:

AppDelegate.swift
import Flutter
import sentiance_event_timeline

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
    
        // Other code
        
        let flutterEngine = SentianceEventTimelinePlugin.initializeListener(
            withEntryPoint: "registerEventTimelineListener",
            libraryURI: "package:your_app_package_name/background.dart"
        )
        // This line below is required for shared_preferences to function
        GeneratedPluginRegistrant.register(with: flutterEngine)
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

For Android, add this code to your custom application class:

MainApplication.kt
import android.app.Application
import com.sentiance.event_timeline_plugin.EventTimelinePlugin

class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        // Other code

        val dartLibrary = "package:your_app_package_name/background.dart"
        EventTimelinePlugin.initializeListener(this, dartLibrary, "registerEventTimelineListener")
    }
}

this page