Skip to content

Commit de5dbc4

Browse files
committed
Add code to update heartbeat
1 parent 2a125d7 commit de5dbc4

6 files changed

Lines changed: 126 additions & 18 deletions

File tree

android/app/src/main/java/com/httpsms/HttpSmsApiService.kt

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,15 @@ import java.util.logging.Logger.getLogger
1515

1616
class HttpSmsApiService(private val apiKey: String) {
1717
private val apiKeyHeader = "x-api-key"
18-
private val baseURL = URI("https://api.httpsms.com")
18+
private val baseURL = URI("https://6a2d-94-246-225-10.ngrok.io")
1919
private val jsonMediaType = "application/json; charset=utf-8".toMediaType()
20+
private val client = OkHttpClient()
2021

2122
init {
2223
getLogger(OkHttpClient::class.java.name).level = Level.FINE
2324
}
2425

2526
fun getOutstandingMessage(messageID: String): Message? {
26-
val client = OkHttpClient()
27-
2827
val request: Request = Request.Builder()
2928
.url(baseURL.resolve("/v1/messages/outstanding?message_id=${messageID}").toURL())
3029
.header(apiKeyHeader, apiKey)
@@ -59,8 +58,6 @@ class HttpSmsApiService(private val apiKey: String) {
5958
}
6059

6160
fun receive(from: String, to: String, content: String, timestamp: ZonedDateTime) {
62-
val client = OkHttpClient()
63-
6461
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'000000'ZZZZZ")
6562
val timestampString = formatter.format(timestamp).replace("+", "Z")
6663

@@ -91,10 +88,31 @@ class HttpSmsApiService(private val apiKey: String) {
9188
Timber.i("received message stored successfully for message with ID [${message?.data?.id}]" )
9289
}
9390

91+
fun storeHeartbeat(phoneNumber: String) {
92+
val body = """
93+
{
94+
"owner": "$phoneNumber"
95+
}
96+
""".trimIndent()
9497

95-
private fun sendEvent(messageId: String, event: String, timestamp: ZonedDateTime, reason: String? = null) {
96-
val client = OkHttpClient()
98+
val request: Request = Request.Builder()
99+
.url(baseURL.resolve("/v1/heartbeats").toURL())
100+
.post(body.toRequestBody(jsonMediaType))
101+
.header(apiKeyHeader, apiKey)
102+
.build()
97103

104+
val response = client.newCall(request).execute()
105+
if (!response.isSuccessful) {
106+
Timber.e("error response [${response.body?.string()}] with code [${response.code}] while sending heartbeat [$body] for owner [$phoneNumber]")
107+
return
108+
}
109+
110+
response.close()
111+
Timber.i( "heartbeat stored successfully for owner [$phoneNumber]" )
112+
}
113+
114+
115+
private fun sendEvent(messageId: String, event: String, timestamp: ZonedDateTime, reason: String? = null) {
98116
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'000000'ZZZZZ")
99117
val timestampString = formatter.format(timestamp).replace("+", "Z")
100118

@@ -129,8 +147,6 @@ class HttpSmsApiService(private val apiKey: String) {
129147

130148

131149
fun updatePhone(phoneNumber: String, fcmToken: String): Boolean {
132-
val client = OkHttpClient()
133-
134150
val body = """
135151
{
136152
"fcm_token": "$fcmToken",
@@ -157,8 +173,6 @@ class HttpSmsApiService(private val apiKey: String) {
157173

158174

159175
fun validateApiKey(): String? {
160-
val client = OkHttpClient()
161-
162176
val request: Request = Request.Builder()
163177
.url(baseURL.resolve("/v1/users/me").toURL())
164178
.header(apiKeyHeader, apiKey)

android/app/src/main/java/com/httpsms/MainActivity.kt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,21 @@ import android.widget.Toast
1616
import androidx.activity.result.contract.ActivityResultContracts
1717
import androidx.appcompat.app.AppCompatActivity
1818
import androidx.core.app.ActivityCompat
19+
import androidx.work.ExistingPeriodicWorkPolicy
20+
import androidx.work.PeriodicWorkRequestBuilder
21+
import androidx.work.WorkManager
1922
import com.google.android.material.button.MaterialButton
2023
import com.google.android.material.switchmaterial.SwitchMaterial
2124
import com.httpsms.services.StickyNotificationService
25+
import com.httpsms.worker.HeartbeatWorker
2226
import timber.log.Timber
27+
import java.time.Instant
28+
import java.time.ZoneId
29+
import java.time.ZoneOffset
30+
import java.time.ZonedDateTime
31+
import java.time.format.DateTimeFormatter
2332
import java.util.*
33+
import java.util.concurrent.TimeUnit
2434

2535

2636
class MainActivity : AppCompatActivity() {
@@ -46,13 +56,48 @@ class MainActivity : AppCompatActivity() {
4656
refreshToken(this)
4757

4858
startStickyNotification(this)
59+
scheduleHeartbeatWorker(this)
60+
setLastHeartbeatTimestamp(this)
4961
}
5062

5163
override fun onResume() {
5264
super.onResume()
5365
Timber.d( "on activity resume")
5466
redirectToLogin()
5567
refreshToken(this)
68+
setLastHeartbeatTimestamp(this)
69+
}
70+
71+
private fun setLastHeartbeatTimestamp(context: Context) {
72+
val refreshTimestampView = findViewById<TextView>(R.id.cardRefreshTime)
73+
val timestamp = Settings.getHeartbeatTimestamp(context)
74+
75+
if (timestamp == 0.toLong()) {
76+
Timber.d("not heartbeat timestamp has been set")
77+
refreshTimestampView.text = "--"
78+
return
79+
}
80+
81+
val timestampZdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC)
82+
val localTime = timestampZdt.withZoneSameInstant(ZoneId.systemDefault())
83+
Timber.d("heartbeat timestamp in UTC is [${timestampZdt}] and local is [$localTime]")
84+
85+
refreshTimestampView.text = localTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
86+
}
87+
88+
private fun scheduleHeartbeatWorker(context: Context) {
89+
val tag = "TAG_HEARTBEAT_WORKER"
90+
91+
val heartbeatWorker =
92+
PeriodicWorkRequestBuilder<HeartbeatWorker>(15, TimeUnit.MINUTES)
93+
.addTag(tag)
94+
.build()
95+
96+
WorkManager
97+
.getInstance(context)
98+
.enqueueUniquePeriodicWork(tag, ExistingPeriodicWorkPolicy.KEEP, heartbeatWorker)
99+
100+
Timber.d("finished scheduling heartbeat worker with ID [${heartbeatWorker.id}]")
56101
}
57102

58103
private fun startStickyNotification(context: Context) {

android/app/src/main/java/com/httpsms/Settings.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ object Settings {
1111
private const val SETTINGS_ACTIVE = "SETTINGS_ACTIVE_STATUS"
1212
private const val SETTINGS_API_KEY = "SETTINGS_API_KEY"
1313
private const val SETTINGS_FCM_TOKEN = "SETTINGS_FCM_TOKEN"
14-
private const val SETTINGS_RECEIVER_REGISTERED = "SETTINGS_RECEIVER_REGISTERED"
1514
private const val SETTINGS_FCM_TOKEN_UPDATE_TIMESTAMP = "SETTINGS_FCM_TOKEN_UPDATE_TIMESTAMP"
15+
private const val SETTINGS_HEARTBEAT_TIMESTAMP = "SETTINGS_HEARTBEAT_TIMESTAMP"
1616

1717
fun getOwner(context: Context): String? {
1818
Timber.d(Settings::getOwner.name)
@@ -137,12 +137,24 @@ object Settings {
137137
.apply()
138138
}
139139

140-
fun setReceiverRegistered(context: Context, registered: Boolean) {
141-
Timber.d(Settings::setReceiverRegistered.name)
140+
fun getHeartbeatTimestamp(context: Context): Long {
141+
Timber.d(Settings::getHeartbeatTimestamp.name)
142+
143+
val timestamp = PreferenceManager
144+
.getDefaultSharedPreferences(context)
145+
.getLong(this.SETTINGS_HEARTBEAT_TIMESTAMP,0)
146+
147+
Timber.d("SETTINGS_HEARTBEAT_TIMESTAMP: [$timestamp]")
148+
return timestamp
149+
}
150+
151+
152+
fun setHeartbeatTimestampAsync(context: Context, timestamp: Long) {
153+
Timber.d(Settings::setHeartbeatTimestampAsync.name)
142154

143155
PreferenceManager.getDefaultSharedPreferences(context)
144156
.edit()
145-
.putBoolean(this.SETTINGS_RECEIVER_REGISTERED, registered)
157+
.putLong(this.SETTINGS_HEARTBEAT_TIMESTAMP, timestamp)
146158
.apply()
147159
}
148160
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.httpsms.worker
2+
3+
import android.content.Context
4+
import androidx.work.Worker
5+
import androidx.work.WorkerParameters
6+
import com.httpsms.HttpSmsApiService
7+
import com.httpsms.Settings
8+
import timber.log.Timber
9+
10+
class HeartbeatWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
11+
override fun doWork(): Result {
12+
Timber.d("executing heartbeat worker")
13+
if (!Settings.isLoggedIn(applicationContext)) {
14+
Timber.w("user is not logged in, stopping processing")
15+
return Result.failure()
16+
}
17+
18+
if (!Settings.getActiveStatus(applicationContext)) {
19+
Timber.w("user is not active, stopping processing")
20+
return Result.failure()
21+
}
22+
23+
HttpSmsApiService(Settings.getApiKeyOrDefault(applicationContext))
24+
.storeHeartbeat(Settings.getOwnerOrDefault(applicationContext))
25+
Timber.d("finished sending heartbeat to server")
26+
27+
Settings.setHeartbeatTimestampAsync(applicationContext, System.currentTimeMillis())
28+
Timber.d("set the heartbeat timestamp")
29+
30+
return Result.success()
31+
}
32+
33+
34+
35+
36+
}

android/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ buildscript {
1212
}
1313

1414
plugins {
15-
id 'com.android.application' version '7.2.1' apply false
16-
id 'com.android.library' version '7.2.1' apply false
15+
id 'com.android.application' version '7.2.2' apply false
16+
id 'com.android.library' version '7.2.2' apply false
1717
id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
1818
}
1919

api/pkg/handlers/heartbeat_handler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func NewHeartbeatHandler(
3939
// RegisterRoutes registers the routes for the MessageHandler
4040
func (h *HeartbeatHandler) RegisterRoutes(router fiber.Router) {
4141
router.Get("/heartbeats", h.Index)
42+
router.Post("/heartbeats", h.Store)
4243
}
4344

4445
// Index returns the heartbeats of a phone number
@@ -108,7 +109,7 @@ func (h *HeartbeatHandler) Store(c *fiber.Ctx) error {
108109
ctxLogger := h.tracer.CtxLogger(h.logger, span)
109110

110111
var request requests.HeartbeatStore
111-
if err := c.QueryParser(&request); err != nil {
112+
if err := c.BodyParser(&request); err != nil {
112113
msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request)
113114
ctxLogger.Warn(stacktrace.Propagate(err, msg))
114115
return h.responseBadRequest(c, err)

0 commit comments

Comments
 (0)