e-ntfy-android/app/src/main/java/io/heckel/ntfy/msg/ApiService.kt

127 lines
4.9 KiB
Kotlin
Raw Normal View History

2021-11-07 19:13:32 +01:00
package io.heckel.ntfy.msg
import android.util.Log
import com.google.gson.Gson
import io.heckel.ntfy.data.Notification
import io.heckel.ntfy.data.topicUrl
2021-11-14 01:26:37 +01:00
import io.heckel.ntfy.data.topicUrlJson
import io.heckel.ntfy.data.topicUrlJsonPoll
2021-11-14 01:26:37 +01:00
import okhttp3.*
import okhttp3.RequestBody.Companion.toRequestBody
2021-11-14 01:26:37 +01:00
import java.io.IOException
import java.util.concurrent.TimeUnit
import kotlin.random.Random
class ApiService {
private val gson = Gson()
private val client = OkHttpClient.Builder()
2021-11-14 01:26:37 +01:00
.callTimeout(15, TimeUnit.SECONDS) // Total timeout for entire request
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build()
private val subscriberClient = OkHttpClient.Builder()
.readTimeout(77, TimeUnit.SECONDS) // Assuming that keepalive messages are more frequent than this
.build()
fun publish(baseUrl: String, topic: String, message: String) {
2021-11-07 19:13:32 +01:00
val url = topicUrl(baseUrl, topic)
Log.d(TAG, "Publishing to $url")
val request = Request.Builder().url(url).put(message.toRequestBody()).build();
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw Exception("Unexpected response ${response.code} when publishing to $url")
2021-11-07 19:13:32 +01:00
}
Log.d(TAG, "Successfully published to $url")
2021-11-07 19:13:32 +01:00
}
}
fun poll(subscriptionId: Long, baseUrl: String, topic: String): List<Notification> {
2021-11-07 19:13:32 +01:00
val url = topicUrlJsonPoll(baseUrl, topic)
Log.d(TAG, "Polling topic $url")
val request = Request.Builder().url(url).build();
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) {
throw Exception("Unexpected response ${response.code} when polling topic $url")
2021-11-07 19:13:32 +01:00
}
val body = response.body?.string()?.trim()
if (body == null || body.isEmpty()) return emptyList()
val notifications = body.lines().map { line ->
fromString(subscriptionId, line)
}
Log.d(TAG, "Notifications: $notifications")
return notifications
2021-11-07 19:13:32 +01:00
}
}
2021-11-16 20:08:52 +01:00
fun subscribe(
baseUrl: String,
topics: String,
since: Long,
notify: (topic: String, Notification) -> Unit,
fail: (Exception) -> Unit
): Call {
2021-11-14 01:26:37 +01:00
val sinceVal = if (since == 0L) "all" else since.toString()
2021-11-16 20:08:52 +01:00
val url = topicUrlJson(baseUrl, topics, sinceVal)
2021-11-14 01:26:37 +01:00
Log.d(TAG, "Opening subscription connection to $url")
val request = Request.Builder().url(url).build()
val call = subscriberClient.newCall(request)
call.enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
try {
if (!response.isSuccessful) {
throw Exception("Unexpected response ${response.code} when subscribing to topic $url")
}
val source = response.body?.source() ?: throw Exception("Unexpected response for $url: body is empty")
while (!source.exhausted()) {
val line = source.readUtf8Line() ?: throw Exception("Unexpected response for $url: line is null")
val message = gson.fromJson(line, Message::class.java)
if (message.event == EVENT_MESSAGE) {
2021-11-16 20:08:52 +01:00
val topic = message.topic
val notification = Notification(
id = message.id,
2021-11-16 20:08:52 +01:00
subscriptionId = 0, // TO BE SET downstream
timestamp = message.time,
message = message.message,
notificationId = Random.nextInt(),
deleted = false
)
2021-11-16 20:08:52 +01:00
notify(topic, notification)
2021-11-14 01:26:37 +01:00
}
}
} catch (e: Exception) {
Log.e(TAG, "Connection to $url failed (1): ${e.message}", e)
2021-11-14 01:26:37 +01:00
fail(e)
}
}
override fun onFailure(call: Call, e: IOException) {
Log.e(TAG, "Connection to $url failed (2): ${e.message}", e)
2021-11-14 01:26:37 +01:00
fail(e)
}
})
return call
}
private fun fromString(subscriptionId: Long, s: String): Notification {
2021-11-14 01:26:37 +01:00
val n = gson.fromJson(s, Message::class.java)
return Notification(n.id, subscriptionId, n.time, n.message, notificationId = 0, deleted = false)
}
2021-11-14 01:26:37 +01:00
private data class Message(
val id: String,
val time: Long,
2021-11-14 01:26:37 +01:00
val event: String,
2021-11-16 20:08:52 +01:00
val topic: String,
val message: String
)
2021-11-07 19:13:32 +01:00
companion object {
private const val TAG = "NtfyApiService"
2021-11-14 01:26:37 +01:00
private const val EVENT_MESSAGE = "message"
2021-11-07 19:13:32 +01:00
}
}