1 // Copyright 2015-present 650 Industries. All rights reserved.
2 package host.exp.exponent.kernel.services.sensors
3 
4 import android.content.Context
5 import android.hardware.SensorEvent
6 import host.exp.exponent.kernel.ExperienceKey
7 import java.lang.ref.WeakReference
8 import java.util.*
9 
10 private var DEFAULT_UPDATE_INTERVAL = 100
11 
12 abstract class SubscribableSensorKernelService internal constructor(reactContext: Context) : BaseSensorKernelService(reactContext) {
13   private val experienceScopeKeyListenersCountMap: MutableMap<String?, Int> = mutableMapOf()
14   private val sensorEventListenerLastUpdateMap: MutableMap<SensorKernelServiceSubscription, Long> = WeakHashMap()
15   private val experienceScopeKeySubscriptionsMap: MutableMap<String?, MutableList<WeakReference<SensorKernelServiceSubscription?>>> = mutableMapOf()
16 
onExperienceForegroundednull17   override fun onExperienceForegrounded(experienceKey: ExperienceKey) {
18     updateObserving()
19   }
20 
onExperienceBackgroundednull21   override fun onExperienceBackgrounded(experienceKey: ExperienceKey) {
22     updateObserving()
23   }
24 
onSensorDataChangednull25   override fun onSensorDataChanged(sensorEvent: SensorEvent) {
26     val currentTime = System.currentTimeMillis()
27     val listeners = experienceScopeKeySubscriptionsMap[currentExperienceKey!!.scopeKey]
28     if (listeners != null) {
29       for (weakReference in listeners) {
30         val sensorKernelServiceSubscription = weakReference.get()
31         if (sensorKernelServiceSubscription != null && sensorKernelServiceSubscription.isEnabled) {
32           var lastUpdate: Long = 0
33           if (sensorEventListenerLastUpdateMap.containsKey(sensorKernelServiceSubscription)) {
34             lastUpdate = sensorEventListenerLastUpdateMap[sensorKernelServiceSubscription]!!
35           }
36           var updateInterval = DEFAULT_UPDATE_INTERVAL.toLong()
37           if (sensorKernelServiceSubscription.updateInterval != null) {
38             updateInterval = sensorKernelServiceSubscription.updateInterval!!
39           }
40           if (currentTime - lastUpdate > updateInterval) {
41             sensorKernelServiceSubscription.sensorEventListener.onSensorDataChanged(sensorEvent)
42             sensorEventListenerLastUpdateMap[sensorKernelServiceSubscription] = currentTime
43           }
44         }
45       }
46     }
47   }
48 
49   // Modules API
createSubscriptionForListenernull50   fun createSubscriptionForListener(
51     experienceKey: ExperienceKey,
52     listener: SensorEventListener
53   ): SensorKernelServiceSubscription {
54     val sensorKernelServiceSubscription =
55       SensorKernelServiceSubscription(experienceKey, this, listener)
56     if (!experienceScopeKeySubscriptionsMap.containsKey(experienceKey.scopeKey)) {
57       experienceScopeKeySubscriptionsMap[experienceKey.scopeKey] = ArrayList()
58     }
59     experienceScopeKeySubscriptionsMap[experienceKey.scopeKey]!!.add(
60       WeakReference(
61         sensorKernelServiceSubscription
62       )
63     )
64     return sensorKernelServiceSubscription
65   }
66 
removeSubscriptionnull67   fun removeSubscription(subscriptionToRemove: SensorKernelServiceSubscription) {
68     sensorEventListenerLastUpdateMap.remove(subscriptionToRemove)
69     val experienceKey = subscriptionToRemove.experienceKey
70     if (experienceScopeKeySubscriptionsMap.containsKey(experienceKey.scopeKey)) {
71       val originalSubscriptions: List<WeakReference<SensorKernelServiceSubscription?>> =
72         experienceScopeKeySubscriptionsMap[experienceKey.scopeKey]!!
73       val leftSubscriptions: MutableList<WeakReference<SensorKernelServiceSubscription?>> =
74         ArrayList()
75       for (subscriptionWeakReference in originalSubscriptions) {
76         val subscription = subscriptionWeakReference.get()
77         if (subscription != null && subscription != subscriptionToRemove) {
78           leftSubscriptions.add(subscriptionWeakReference)
79         }
80       }
81       if (leftSubscriptions.size > 0) {
82         experienceScopeKeySubscriptionsMap[experienceKey.scopeKey] = leftSubscriptions
83       } else {
84         experienceScopeKeySubscriptionsMap.remove(experienceKey.scopeKey)
85       }
86     }
87   }
88 
89   // SensorKernelServiceSubscription API
onSubscriptionEnabledChangednull90   fun onSubscriptionEnabledChanged(sensorKernelServiceSubscription: SensorKernelServiceSubscription) {
91     val experienceKey = sensorKernelServiceSubscription.experienceKey
92     val enabledListenersCount = getEnabledListenersForExperienceKey(experienceKey)
93     if (sensorKernelServiceSubscription.isEnabled) {
94       experienceScopeKeyListenersCountMap[experienceKey.scopeKey] = enabledListenersCount + 1
95     } else {
96       experienceScopeKeyListenersCountMap[experienceKey.scopeKey] = enabledListenersCount - 1
97     }
98     if (getEnabledListenersForExperienceKey(experienceKey) == 0) {
99       experienceScopeKeyListenersCountMap.remove(experienceKey.scopeKey)
100     }
101     updateObserving()
102   }
103 
104   // android.hardware.SensorEventListener
onSensorChangednull105   override fun onSensorChanged(sensorEvent: SensorEvent) {
106     if (sensorEvent.sensor.type == sensorType) {
107       onSensorDataChanged(sensorEvent)
108     }
109   }
110 
111   // Private helpers
getEnabledListenersForExperienceKeynull112   private fun getEnabledListenersForExperienceKey(experienceKey: ExperienceKey?): Int {
113     // null is an expected key, or at least that's how the code was written. may be a bug.
114     val mapKey = experienceKey?.scopeKey
115     return if (experienceScopeKeyListenersCountMap.containsKey(mapKey)) {
116       experienceScopeKeyListenersCountMap[mapKey]!!
117     } else 0
118   }
119 
cleanWeakSubscriptionsListnull120   private fun cleanWeakSubscriptionsList(experienceKey: ExperienceKey?) {
121     // null is an expected key, or at least that's how the code was written. may be a bug.
122     val mapKey = experienceKey?.scopeKey
123     val listeners: List<WeakReference<SensorKernelServiceSubscription?>>? =
124       experienceScopeKeySubscriptionsMap[mapKey]
125     val realListeners: MutableList<WeakReference<SensorKernelServiceSubscription?>> = ArrayList()
126     if (listeners != null) {
127       for (subscriptionWeakReference in listeners) {
128         if (subscriptionWeakReference.get() != null) {
129           realListeners.add(subscriptionWeakReference)
130         }
131       }
132     }
133     if (realListeners.size > 0) {
134       experienceScopeKeySubscriptionsMap[mapKey] = realListeners
135     } else {
136       experienceScopeKeySubscriptionsMap.remove(mapKey)
137     }
138   }
139 
updateObservingnull140   private fun updateObserving() {
141     cleanWeakSubscriptionsList(currentExperienceKey)
142 
143     // Start/stop observing according to the experience state
144     if (getEnabledListenersForExperienceKey(currentExperienceKey) > 0) {
145       super.startObserving()
146     } else {
147       super.stopObserving()
148     }
149   }
150 }
151