Switch from RabbitMQ to server-sent events
(not fully working yet)
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package de.nicolasklier.livebeat
|
||||
|
||||
import android.util.Log
|
||||
import okhttp3.Response
|
||||
import okhttp3.internal.platform.Platform
|
||||
import okhttp3.internal.platform.Platform.Companion.INFO
|
||||
import okhttp3.sse.EventSource
|
||||
import okhttp3.sse.EventSourceListener
|
||||
import okio.IOException
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import java.util.concurrent.BlockingQueue
|
||||
import java.util.concurrent.LinkedBlockingDeque
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class EventSourceRecorder : EventSourceListener() {
|
||||
private val events: BlockingQueue<Any> = LinkedBlockingDeque()
|
||||
|
||||
override fun onOpen(eventSource: EventSource, response: Response) {
|
||||
Log.i("SSE", "Connection opened!")
|
||||
}
|
||||
|
||||
override fun onEvent(
|
||||
eventSource: EventSource, @Nullable id: String?, @Nullable type: String?,
|
||||
data: String
|
||||
) {
|
||||
Log.i("SSE", "onEvent: " + data)
|
||||
}
|
||||
|
||||
override fun onClosed(eventSource: EventSource) {
|
||||
Log.i("SSE", "Connection to SSE got closed!")
|
||||
events.add(Closed())
|
||||
}
|
||||
|
||||
fun onFailure(
|
||||
eventSource: EventSource?,
|
||||
@Nullable t: Throwable
|
||||
) {
|
||||
Platform.get().log("[ES] onFailure", INFO, t)
|
||||
}
|
||||
|
||||
private fun nextEvent(): Any {
|
||||
return try {
|
||||
val event: Any = events.poll(10, TimeUnit.SECONDS)
|
||||
?: throw AssertionError("Timed out waiting for event.")
|
||||
event
|
||||
} catch (e: InterruptedException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
internal class Open(val eventSource: EventSource?, response: Response) {
|
||||
val response: Response
|
||||
override fun toString(): String {
|
||||
return "Open[$response]"
|
||||
}
|
||||
|
||||
init {
|
||||
this.response = response
|
||||
}
|
||||
}
|
||||
|
||||
internal class Failure(val t: Throwable, response: Response?) {
|
||||
val response: Response?
|
||||
val responseBody: String?
|
||||
override fun toString(): String {
|
||||
return if (response == null) {
|
||||
"Failure[$t]"
|
||||
} else "Failure[$response]"
|
||||
}
|
||||
|
||||
init {
|
||||
this.response = response
|
||||
var responseBody: String? = null
|
||||
if (response != null) {
|
||||
try {
|
||||
responseBody = response.body.toString()
|
||||
} catch (ignored: IOException) {
|
||||
}
|
||||
}
|
||||
this.responseBody = responseBody
|
||||
}
|
||||
}
|
||||
|
||||
internal class Closed {
|
||||
override fun toString(): String {
|
||||
return "Closed[]"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package de.nicolasklier.livebeat
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import okhttp3.sse.EventSource
|
||||
import okhttp3.sse.EventSources
|
||||
import java.lang.Error
|
||||
import java.net.ConnectException
|
||||
|
||||
class HttpRequests {
|
||||
companion object {
|
||||
fun get(url: String, sendToken: Boolean = true): Response {
|
||||
val client = OkHttpClient()
|
||||
var req = Request.Builder()
|
||||
.url(url)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
if (sendToken) {
|
||||
req = req.newBuilder().addHeader("token", MainActivity.TOKEN).build();
|
||||
}
|
||||
return client.newCall(req).execute();
|
||||
}
|
||||
|
||||
fun post(url: String, body: String, sendToken: Boolean = true): Response {
|
||||
try {
|
||||
val client = OkHttpClient()
|
||||
var req = Request.Builder()
|
||||
.url(url)
|
||||
.post(
|
||||
(body).toRequestBody()
|
||||
)
|
||||
.header("Content-Type", "application/json")
|
||||
.build()
|
||||
|
||||
if (sendToken) {
|
||||
req = req.newBuilder().addHeader("token", MainActivity.TOKEN).build()
|
||||
}
|
||||
return client.newCall(req).execute()
|
||||
} catch (e: ConnectException) {
|
||||
throw Error("Connection to $url couldn't be made: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
fun sse(url: String) {
|
||||
val client = OkHttpClient()
|
||||
val req = Request.Builder().url(url).build();
|
||||
|
||||
val handler = EventSourceRecorder()
|
||||
val factory = EventSources.createFactory(client)
|
||||
val sse = factory.newEventSource(req, handler)
|
||||
|
||||
sse.request()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
@@ -31,6 +32,7 @@ import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.net.ConnectException
|
||||
import java.util.logging.Logger
|
||||
|
||||
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||
@@ -49,13 +51,13 @@ class MainActivity : AppCompatActivity() {
|
||||
if (TOKEN == "") return;
|
||||
Thread(Runnable {
|
||||
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
||||
val client = OkHttpClient()
|
||||
/*val client = OkHttpClient()
|
||||
val req = Request.Builder()
|
||||
.url("$API_URL/phone/$androidId")
|
||||
.header("token", TOKEN)
|
||||
.get()
|
||||
.build()
|
||||
val response = client.newCall(req).execute()
|
||||
.build()*/
|
||||
val response = HttpRequests.get("$API_URL/phone/$androidId")
|
||||
|
||||
if (response.code != 200) {
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Device isn't registered yet. Registering ...", Snackbar.LENGTH_SHORT)
|
||||
@@ -75,7 +77,7 @@ class MainActivity : AppCompatActivity() {
|
||||
val phoneToJson = moshi.adapter(Phone::class.java)
|
||||
val json = phoneToJson.toJson(phone)
|
||||
|
||||
val createPhone = Request.Builder()
|
||||
/*val createPhone = Request.Builder()
|
||||
.url("$API_URL/phone")
|
||||
.post(
|
||||
(json).toRequestBody()
|
||||
@@ -83,7 +85,8 @@ class MainActivity : AppCompatActivity() {
|
||||
.header("Content-Type", "application/json")
|
||||
.header("token", TOKEN)
|
||||
.build()
|
||||
client.newCall(createPhone).execute()
|
||||
client.newCall(createPhone).execute()*/
|
||||
HttpRequests.post("$API_URL/phone", json);
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
@@ -95,7 +98,7 @@ class MainActivity : AppCompatActivity() {
|
||||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(findViewById(R.id.toolbar))
|
||||
|
||||
val process = Runtime.getRuntime().exec("su")
|
||||
//val process = Runtime.getRuntime().exec("su")
|
||||
|
||||
// Check authorization
|
||||
val backendChecks = Thread(Runnable {
|
||||
@@ -104,15 +107,40 @@ class MainActivity : AppCompatActivity() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonToLogin = moshi.adapter(Login::class.java)
|
||||
|
||||
val token = "{ \"username\": \"" + username + "\"," +
|
||||
"\"password\": \"" + password + "\" }"
|
||||
try {
|
||||
HttpRequests.post("$API_URL/user/login", token)
|
||||
} catch (e: Error) {
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Backend server is not available", Snackbar.LENGTH_SHORT)
|
||||
.setBackgroundTint(Color.RED)
|
||||
.setActionTextColor(Color.WHITE)
|
||||
.show();
|
||||
return@Runnable
|
||||
}
|
||||
val client = OkHttpClient()
|
||||
val req = Request.Builder()
|
||||
.url("$API_URL/user/login")
|
||||
.post(
|
||||
("{ \"username\": \"" + username + "\"," +
|
||||
"\"password\": \"" + password + "\" }").toRequestBody()
|
||||
(token).toRequestBody()
|
||||
)
|
||||
.header("Content-Type", "application/json")
|
||||
.build()
|
||||
|
||||
// Check if server is available.
|
||||
try {
|
||||
val testReq = Request.Builder()
|
||||
.url("$API_URL/user/login")
|
||||
.post(
|
||||
("{ \"username\": \"" + username + "\"," +
|
||||
"\"password\": \"" + password + "\" }").toRequestBody()
|
||||
)
|
||||
.header("Content-Type", "application/json")
|
||||
.build()
|
||||
client.newCall(testReq).execute();
|
||||
} catch (e: ConnectException) {
|
||||
}
|
||||
|
||||
val loginResponse = client.newCall(req).execute()
|
||||
val responseBody = loginResponse.body!!.string()
|
||||
|
||||
@@ -141,8 +169,10 @@ class MainActivity : AppCompatActivity() {
|
||||
val userInfoResponseBody = userinfoResponse.body!!.string()
|
||||
USER = jsonToUser.fromJson(userInfoResponseBody)
|
||||
|
||||
val intent = Intent(this, TrackerService::class.java)
|
||||
|
||||
// Only start service if authentication went good.
|
||||
startService(Intent(this, TrackerService::class.java))
|
||||
startService(intent)
|
||||
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Login succeeded", Snackbar.LENGTH_SHORT)
|
||||
.setBackgroundTint(Color.GREEN)
|
||||
@@ -171,17 +201,8 @@ class MainActivity : AppCompatActivity() {
|
||||
this.broadcastReceiver = object : BroadcastReceiver() {
|
||||
@SuppressLint("CutPasteId")
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val statusRabbit = intent.getBooleanExtra("statusRabbit", false)
|
||||
val statusHttp = intent.getIntExtra("statusHttp", 404)
|
||||
|
||||
if (statusRabbit) {
|
||||
findViewById<TextView>(R.id.rabbitStatus).text = "connected"
|
||||
findViewById<TextView>(R.id.rabbitStatus).setTextColor(Color.GREEN)
|
||||
} else {
|
||||
findViewById<TextView>(R.id.rabbitStatus).text = "disconnected"
|
||||
findViewById<TextView>(R.id.rabbitStatus).setTextColor(Color.RED)
|
||||
}
|
||||
|
||||
/*if (statusHttp == 200) {
|
||||
findViewById<TextView>(R.id.httpStatus).text = "ONLINE (no login)"
|
||||
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.CYAN)
|
||||
|
||||
@@ -27,7 +27,7 @@ class User(
|
||||
val type: String,
|
||||
val lastLogin: String,
|
||||
val twoFASecret: String?,
|
||||
val brokerToken: String,
|
||||
val eventToken: String,
|
||||
val createdAt: String
|
||||
)
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.location.LocationManager
|
||||
import android.os.BatteryManager
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
@@ -18,14 +17,9 @@ import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.rabbitmq.client.Channel
|
||||
import com.rabbitmq.client.Connection
|
||||
import com.rabbitmq.client.ConnectionFactory
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
class TrackerService : Service() {
|
||||
|
||||
@@ -36,38 +30,9 @@ class TrackerService : Service() {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun connectWithBroker() {
|
||||
// This thread only connects to RabbitMQ
|
||||
val connectionThread = Thread(Runnable {
|
||||
val client = OkHttpClient()
|
||||
|
||||
val factory = ConnectionFactory()
|
||||
factory.username = MainActivity.USER!!.name
|
||||
factory.password = MainActivity.USER!!.brokerToken
|
||||
factory.virtualHost = "/"
|
||||
factory.host = "192.168.178.26"
|
||||
factory.port = 5672
|
||||
factory.isAutomaticRecoveryEnabled = true
|
||||
try {
|
||||
conn[0] = factory.newConnection()
|
||||
channel[0] = conn[0]?.createChannel()
|
||||
|
||||
val intent = Intent("de.nicolasklier.livebeat")
|
||||
val bundle = Bundle()
|
||||
bundle.putBoolean("statusRabbit", true)
|
||||
intent.putExtras(bundle)
|
||||
this.sendBroadcast(intent)
|
||||
|
||||
channel[0]?.queueDeclare("tracker-" + factory.username, true, false, false, null)
|
||||
//channel[0]?.basicPublish("", "Tracker", null, "Test message".toByteArray())
|
||||
Log.i("RabbitMQ", "run: Published test message")
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: TimeoutException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
})
|
||||
connectionThread.start()
|
||||
@SuppressLint("CheckResult")
|
||||
private fun subscribeToEvents() {
|
||||
HttpRequests.sse("http://192.168.178.26/user/events?token=${MainActivity.USER?.eventToken}")
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
@@ -105,7 +70,7 @@ class TrackerService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
connectWithBroker()
|
||||
subscribeToEvents()
|
||||
startForeground()
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user