From 77e4df06f849c3d324bce444e379f4ea2b376f3a Mon Sep 17 00:00:00 2001 From: Atridad Lahiji Date: Fri, 22 Aug 2025 20:59:36 -0600 Subject: [PATCH] 1.1.1 - More fixes for notification reliability --- app/build.gradle.kts | 4 +- app/src/main/AndroidManifest.xml | 4 +- .../service/SessionTrackingService.kt | 116 +++++++----------- app/src/main/res/values/strings.xml | 1 + 4 files changed, 50 insertions(+), 75 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8438f58..6562bf0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,8 +14,8 @@ android { applicationId = "com.atridad.openclimb" minSdk = 33 targetSdk = 36 - versionCode = 15 - versionName = "1.1.0" + versionCode = 16 + versionName = "1.1.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9157e08..f5ee4fe 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,7 +12,6 @@ - + android:foregroundServiceType="specialUse" + android:description="@string/session_tracking_service_description"> diff --git a/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt b/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt index 269a492..be28811 100644 --- a/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt +++ b/app/src/main/java/com/atridad/openclimb/service/SessionTrackingService.kt @@ -7,7 +7,6 @@ import android.app.Service import android.content.Context import android.content.Intent import android.os.IBinder -import android.os.PowerManager import androidx.core.app.NotificationCompat import com.atridad.openclimb.MainActivity import com.atridad.openclimb.R @@ -17,13 +16,13 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.firstOrNull import java.time.LocalDateTime import java.time.temporal.ChronoUnit +import kotlinx.coroutines.runBlocking class SessionTrackingService : Service() { private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationJob: Job? = null private var monitoringJob: Job? = null - private var wakeLock: PowerManager.WakeLock? = null private lateinit var repository: ClimbRepository private lateinit var notificationManager: NotificationManager @@ -58,7 +57,6 @@ class SessionTrackingService : Service() { notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager createNotificationChannel() - acquireWakeLock() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -87,59 +85,51 @@ class SessionTrackingService : Service() { } } } - // Return START_STICKY to restart service if it gets killed - return START_STICKY + + return START_REDELIVER_INTENT } override fun onTaskRemoved(rootIntent: Intent?) { super.onTaskRemoved(rootIntent) - // If the app is removed from recent tasks, ensure the service keeps running - // This helps maintain the notification even if the user swipes away the app - } - - override fun onLowMemory() { - super.onLowMemory() - // Don't stop the service on low memory, just log it - // The notification is important for user experience } override fun onBind(intent: Intent?): IBinder? = null private fun startSessionTracking(sessionId: String) { - // Cancel any existing jobs notificationJob?.cancel() monitoringJob?.cancel() - // Start the main notification update job + try { + createAndShowNotification(sessionId) + } catch (e: Exception) { + e.printStackTrace() + } + notificationJob = serviceScope.launch { try { - // Initial notification update - updateNotification(sessionId) + if (!isNotificationActive()) { + delay(1000L) + createAndShowNotification(sessionId) + } - // Update every 2 seconds for better performance while (isActive) { - delay(2000L) + delay(5000L) updateNotification(sessionId) } } catch (e: Exception) { - // Log error and continue e.printStackTrace() } } - // Start the monitoring job that ensures notification stays active monitoringJob = serviceScope.launch { try { while (isActive) { - delay(5000L) // Check every 5 seconds + delay(10000L) - // Verify the notification is still active if (!isNotificationActive()) { - // Notification was dismissed, recreate it updateNotification(sessionId) } - // Verify the session is still active val session = repository.getSessionById(sessionId) if (session == null || session.status != com.atridad.openclimb.data.model.SessionStatus.ACTIVE) { stopSessionTracking() @@ -155,7 +145,6 @@ class SessionTrackingService : Service() { private fun stopSessionTracking() { notificationJob?.cancel() monitoringJob?.cancel() - releaseWakeLock() stopForeground(STOP_FOREGROUND_REMOVE) stopSelf() } @@ -171,14 +160,37 @@ class SessionTrackingService : Service() { private suspend fun updateNotification(sessionId: String) { try { - val session = repository.getSessionById(sessionId) + createAndShowNotification(sessionId) + } catch (e: Exception) { + e.printStackTrace() + + try { + delay(10000L) + createAndShowNotification(sessionId) + } catch (retryException: Exception) { + retryException.printStackTrace() + stopSessionTracking() + } + } + } + + private fun createAndShowNotification(sessionId: String) { + try { + val session = runBlocking { + repository.getSessionById(sessionId) + } if (session == null || session.status != com.atridad.openclimb.data.model.SessionStatus.ACTIVE) { stopSessionTracking() return } - val gym = repository.getGymById(session.gymId) - val attempts = repository.getAttemptsBySession(sessionId).firstOrNull() ?: emptyList() + val gym = runBlocking { + repository.getGymById(session.gymId) + } + + val attempts = runBlocking { + repository.getAttemptsBySession(sessionId).firstOrNull() ?: emptyList() + } val duration = session.startTime?.let { startTime -> try { @@ -205,7 +217,7 @@ class SessionTrackingService : Service() { .setSmallIcon(R.drawable.ic_mountains) .setOngoing(true) .setAutoCancel(false) - .setPriority(NotificationCompat.PRIORITY_LOW) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setCategory(NotificationCompat.CATEGORY_SERVICE) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setContentIntent(createOpenAppIntent()) @@ -221,24 +233,13 @@ class SessionTrackingService : Service() { ) .build() - // Always start foreground to ensure service stays alive startForeground(NOTIFICATION_ID, notification) - // Also notify separately to ensure it's visible notificationManager.notify(NOTIFICATION_ID, notification) } catch (e: Exception) { e.printStackTrace() - // Don't stop the service on notification errors, just log them - // Try to restart the notification after a delay - try { - delay(5000L) - updateNotification(sessionId) - } catch (retryException: Exception) { - retryException.printStackTrace() - // If retry fails, stop the service to prevent infinite loops - stopSessionTracking() - } + throw e } } @@ -269,50 +270,23 @@ class SessionTrackingService : Service() { val channel = NotificationChannel( CHANNEL_ID, "Session Tracking", - NotificationManager.IMPORTANCE_LOW + NotificationManager.IMPORTANCE_DEFAULT ).apply { description = "Shows active climbing session information" setShowBadge(false) lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC enableLights(false) enableVibration(false) + setSound(null, null) } notificationManager.createNotificationChannel(channel) } - private fun acquireWakeLock() { - try { - val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager - wakeLock = powerManager.newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, - "OpenClimb:SessionTrackingWakeLock" - ).apply { - acquire(10*60*1000L) // 10 minutes timeout - } - } catch (e: Exception) { - e.printStackTrace() - } - } - - private fun releaseWakeLock() { - try { - wakeLock?.let { - if (it.isHeld) { - it.release() - } - } - wakeLock = null - } catch (e: Exception) { - e.printStackTrace() - } - } - override fun onDestroy() { super.onDestroy() notificationJob?.cancel() monitoringJob?.cancel() - releaseWakeLock() serviceScope.cancel() } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 93d5beb..7ef7531 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,4 @@ OpenClimb + Tracks active climbing sessions and displays session information in the notification area \ No newline at end of file