1 // Copyright 2015-present 650 Industries. All rights reserved. 2 package host.exp.exponent.kernel.services 3 4 import host.exp.exponent.di.NativeModuleDepsProvider 5 import host.exp.exponent.kernel.ExperienceKey 6 import host.exp.exponent.storage.ExponentSharedPreferences 7 import org.json.JSONException 8 import org.json.JSONObject 9 import java.util.* 10 import javax.inject.Inject 11 12 private const val FIVE_MINUTES_MS = (5 * 60 * 1000).toLong() 13 private const val AUTO_RELOAD_BUFFER_BASE_MS = (5 * 1000).toLong() 14 15 class ErrorRecoveryManager(private val experienceKey: ExperienceKey?) { 16 private var timeLastLoaded = 0L 17 private var didError = false 18 19 @Inject 20 lateinit var exponentSharedPreferences: ExponentSharedPreferences 21 markExperienceLoadednull22 fun markExperienceLoaded() { 23 timeLastLoaded = System.currentTimeMillis() 24 timeAnyExperienceLoaded = timeLastLoaded 25 markErrored(false) 26 } 27 28 @JvmOverloads markErrorednull29 fun markErrored(didError: Boolean = true) { 30 this.didError = didError 31 if (experienceKey != null) { 32 val metadata = exponentSharedPreferences.getExperienceMetadata(experienceKey) ?: JSONObject() 33 try { 34 metadata.put(ExponentSharedPreferences.EXPERIENCE_METADATA_LOADING_ERROR, didError) 35 exponentSharedPreferences.updateExperienceMetadata(experienceKey, metadata) 36 } catch (e: JSONException) { 37 e.printStackTrace() 38 } 39 } 40 } 41 shouldReloadOnErrornull42 fun shouldReloadOnError(): Boolean { 43 val diff = System.currentTimeMillis() - timeLastLoaded 44 val reloadBuffer = reloadBuffer() 45 return diff >= reloadBuffer 46 } 47 reloadBuffernull48 private fun reloadBuffer(): Long { 49 var interval = Math.min( 50 FIVE_MINUTES_MS, 51 ( 52 AUTO_RELOAD_BUFFER_BASE_MS * Math.pow( 53 1.5, 54 reloadBufferDepth.toDouble() 55 ) 56 ).toLong() 57 ) 58 val timeSinceLastExperienceLoaded = System.currentTimeMillis() - timeAnyExperienceLoaded 59 if (timeSinceLastExperienceLoaded > interval * 2) { 60 reloadBufferDepth = 0 61 interval = AUTO_RELOAD_BUFFER_BASE_MS 62 } 63 return interval 64 } 65 66 companion object { 67 private val experienceScopeKeyToManager: MutableMap<String, ErrorRecoveryManager> = HashMap() 68 private var timeAnyExperienceLoaded: Long = 0 69 70 // This goes up when there are a bunch of errors in succession 71 private var reloadBufferDepth: Long = 0 72 getInstancenull73 @JvmStatic fun getInstance(experienceKey: ExperienceKey): ErrorRecoveryManager { 74 if (!experienceScopeKeyToManager.containsKey(experienceKey.scopeKey)) { 75 experienceScopeKeyToManager[experienceKey.scopeKey] = ErrorRecoveryManager(experienceKey) 76 } 77 return experienceScopeKeyToManager[experienceKey.scopeKey]!! 78 } 79 } 80 81 init { 82 NativeModuleDepsProvider.instance.inject(ErrorRecoveryManager::class.java, this) 83 } 84 } 85