package com.gramiatechnologies.gramia import android.content.Context import android.util.Log import androidx.work.Worker import androidx.work.WorkerParameters import okhttp3.* import org.json.JSONObject import java.util.concurrent.TimeUnit class NotificationWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) { private fun shownNotificationsKey(institutionUserId: String): String { return "shown_notification_ids_$institutionUserId" } private fun readShownNotificationIds(institutionUserId: String): LinkedHashSet { val prefs = applicationContext.getSharedPreferences("easiwrap_prefs", Context.MODE_PRIVATE) val stored = prefs.getStringSet(shownNotificationsKey(institutionUserId), emptySet()).orEmpty() return LinkedHashSet(stored) } private fun writeShownNotificationIds(institutionUserId: String, ids: LinkedHashSet) { val prefs = applicationContext.getSharedPreferences("easiwrap_prefs", Context.MODE_PRIVATE) while (ids.size > 500) { val iterator = ids.iterator() if (!iterator.hasNext()) { break } iterator.next() iterator.remove() } prefs.edit() .putStringSet(shownNotificationsKey(institutionUserId), LinkedHashSet(ids)) .apply() } override fun doWork(): Result { val institutionUserId = inputData.getString("institution_user_id") ?: return Result.failure() val useriD = inputData.getString("userId") ?: return Result.failure() val apiToken = inputData.getString("apiToken").orEmpty() val shownNotificationIds = readShownNotificationIds(institutionUserId) val client = OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build() checkForNativeAppUpdate(client) val url = "${ApiConfig.BASE_URL}/tray-notification-count" val formBody = FormBody.Builder() .add("institution_user", institutionUserId) .add("api", "true") .add("user", useriD) .add("clear", "true") .build() val requestBuilder = Request.Builder() .url(url) .post(formBody) if (apiToken.isNotBlank()) { requestBuilder.addHeader("X-Gramia-Api-Token", apiToken) } val request = requestBuilder.build() try { val response = client.newCall(request).execute() val responseBody = response.body?.string() Log.d("NotificationWorker", "Poll response code=${response.code}") if (!response.isSuccessful) { Log.w("NotificationWorker", "Notification poll HTTP ${response.code}") return if (response.code >= 500 || response.code == 429) Result.retry() else Result.success() } val json = JSONObject(responseBody ?: "{}") val count = json.optInt("count", 0) if (count > 0 && json.has("records")) { val records = json.getJSONArray("records") for (i in 0 until records.length()) { val notif = records.getJSONObject(i) val notificationId = notif.optInt("iD", 0) val notificationKey = if (notificationId > 0) { notificationId.toString() } else { "${notif.optString("title", "")}|${notif.optString("message", "")}" } if (notificationKey.isBlank() || shownNotificationIds.contains(notificationKey)) { continue } val title = notif.optString("title", "Gramia") val message = notif.optString("message", "") if (message.isBlank()) continue NotificationUtils.showNotification( applicationContext, title, message, if (notificationId > 0) notificationId else notificationKey.hashCode() ) shownNotificationIds.add(notificationKey) } writeShownNotificationIds(institutionUserId, shownNotificationIds) } return Result.success() } catch (e: Exception) { Log.w("NotificationWorker", "Notification poll failed", e) return Result.retry() } } private fun checkForNativeAppUpdate(client: OkHttpClient) { try { val packageInfo = applicationContext.packageManager.getPackageInfo(applicationContext.packageName, 0) val currentVersionCode = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { packageInfo.longVersionCode } else { @Suppress("DEPRECATION") packageInfo.versionCode.toLong() } val request = Request.Builder() .url("${ApiConfig.BASE_URL}/api/mobile-app-update?platform=android&version_code=$currentVersionCode") .get() .build() client.newCall(request).execute().use { response -> if (!response.isSuccessful) return val json = JSONObject(response.body?.string().orEmpty()) val latestVersionCode = json.optLong("latest_version_code", currentVersionCode) if (!json.optBoolean("update_available", false) || latestVersionCode <= currentVersionCode) return val prefs = applicationContext.getSharedPreferences("gramia_app_updates", Context.MODE_PRIVATE) if (prefs.getLong("last_notified_version_code", -1L) == latestVersionCode) return val title = json.optString("title", "Update Gramia") val message = json.optString("message", "A new Gramia version is ready to install.") NotificationUtils.showNotification( applicationContext, title, message, ("app-update-$latestVersionCode").hashCode(), openAppUpdate = true ) prefs.edit().putLong("last_notified_version_code", latestVersionCode).apply() } } catch (error: Exception) { // Update discovery must never prevent ordinary notification polling. Log.w("NotificationWorker", "Background app update check failed", error) } } }