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