@@ -12,6 +12,10 @@ import com.github.gotify.client.model.Message
1212import java.util.Calendar
1313import java.util.concurrent.TimeUnit
1414import java.util.concurrent.atomic.AtomicLong
15+ import kotlin.math.pow
16+ import kotlin.time.Duration
17+ import kotlin.time.Duration.Companion.minutes
18+ import kotlin.time.Duration.Companion.seconds
1519import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
1620import okhttp3.OkHttpClient
1721import okhttp3.Request
@@ -25,8 +29,8 @@ internal class WebSocketConnection(
2529 settings : SSLSettings ,
2630 private val token : String? ,
2731 private val alarmManager : AlarmManager ,
28- private val reconnectInterval : Int ,
29- private val disableBackoff : Boolean
32+ private val reconnectDelay : Duration ,
33+ private val exponentialBackoff : Boolean
3034) {
3135 companion object {
3236 private val ID = AtomicLong (0 )
@@ -130,19 +134,19 @@ internal class WebSocketConnection(
130134 state = State .Disconnected
131135 }
132136
133- fun scheduleReconnectNow (seconds : Long ) = scheduleReconnect(ID .get(), seconds )
137+ fun scheduleReconnectNow (scheduleIn : Duration ) = scheduleReconnect(ID .get(), scheduleIn )
134138
135139 @Synchronized
136- fun scheduleReconnect (id : Long , seconds : Long ) {
140+ fun scheduleReconnect (id : Long , scheduleIn : Duration ) {
137141 if (state == State .Connecting || state == State .Connected ) {
138142 return
139143 }
140144 state = State .Scheduled
141145
142146 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N ) {
143- Logger .info(" WebSocket: scheduling a restart in $seconds second(s) (via alarm manager)" )
147+ Logger .info(" WebSocket: scheduling a restart in $scheduleIn (via alarm manager)" )
144148 val future = Calendar .getInstance()
145- future.add(Calendar .SECOND , seconds .toInt())
149+ future.add(Calendar .SECOND , scheduleIn.inWholeSeconds .toInt())
146150
147151 alarmManagerCallback?.run (alarmManager::cancel)
148152 val cb = OnAlarmListener { syncExec(id) { start() } }
@@ -155,11 +159,11 @@ internal class WebSocketConnection(
155159 null
156160 )
157161 } else {
158- Logger .info(" WebSocket: scheduling a restart in $seconds second(s) " )
162+ Logger .info(" WebSocket: scheduling a restart in $scheduleIn " )
159163 handlerCallback?.run (reconnectHandler::removeCallbacks)
160164 val cb = Runnable { syncExec(id) { start() } }
161165 handlerCallback = cb
162- reconnectHandler.postDelayed(cb, TimeUnit . SECONDS .toMillis(seconds) )
166+ reconnectHandler.postDelayed(cb, scheduleIn.inWholeMilliseconds )
163167 }
164168 }
165169
@@ -206,16 +210,15 @@ internal class WebSocketConnection(
206210 closed()
207211
208212 errorCount++
209- val seconds = if (disableBackoff) {
210- reconnectInterval
211- } else {
212- ((errorCount * 2 - 1 ) * reconnectInterval)
213- .coerceAtMost(TimeUnit .MINUTES .toSeconds(20 ).toInt())
213+
214+ var scheduleIn = reconnectDelay
215+ if (exponentialBackoff) {
216+ scheduleIn * = 2.0 .pow(errorCount - 1 )
214217 }
218+ scheduleIn = scheduleIn.coerceIn(5 .seconds.. 20 .minutes)
215219
216- val rounded = seconds.coerceAtLeast(5 )
217- onFailure.execute(response?.message ? : " unreachable" , rounded)
218- scheduleReconnect(id, rounded.toLong())
220+ onFailure.execute(response?.message ? : " unreachable" , scheduleIn)
221+ scheduleReconnect(id, scheduleIn)
219222 }
220223 super .onFailure(webSocket, t, response)
221224 }
@@ -229,7 +232,7 @@ internal class WebSocketConnection(
229232 }
230233
231234 internal fun interface OnNetworkFailureRunnable {
232- fun execute (status : String , seconds : Int )
235+ fun execute (status : String , reconnectIn : Duration )
233236 }
234237
235238 internal enum class State {
0 commit comments