Refactor code structure for improved readability and maintainability
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 5m34s
Build / build-windows (push) Successful in 6m35s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled

This commit is contained in:
2026-01-25 04:20:38 +11:00
parent 6a32c023ee
commit eaa8e3949a
5 changed files with 99 additions and 4 deletions

2
cinny

Submodule cinny updated: 3efc68e893...d226b1810a

View File

@@ -8,6 +8,8 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<!-- AndroidTV support -->
<uses-feature android:name="android.software.leanback" android:required="false" />

View File

@@ -1,5 +1,7 @@
package wtf.ruv.paarrot
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
@@ -7,5 +9,17 @@ class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
// Start the foreground sync service to keep the app alive for notifications
startSyncService()
}
private fun startSyncService() {
val serviceIntent = Intent(this, SyncService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
}
}

View File

@@ -1,5 +1,6 @@
package wtf.ruv.paarrot
import android.app.AlarmManager
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
@@ -7,8 +8,11 @@ import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.PowerManager
import android.os.SystemClock
import androidx.core.app.NotificationCompat
/**
@@ -21,9 +25,19 @@ class SyncService : Service() {
private const val CHANNEL_ID = "paarrot_sync_channel"
private const val CHANNEL_NAME = "Background Sync"
private const val WAKE_LOCK_TAG = "Paarrot:SyncWakeLock"
private const val ALARM_REQUEST_CODE = 1001
private const val KEEP_ALIVE_INTERVAL_MS = 5 * 60 * 1000L // 5 minutes
}
private var wakeLock: PowerManager.WakeLock? = null
private val handler = Handler(Looper.getMainLooper())
private val keepAliveRunnable = object : Runnable {
override fun run() {
// Re-acquire wake lock periodically to prevent it from being released
ensureWakeLock()
handler.postDelayed(this, KEEP_ALIVE_INTERVAL_MS)
}
}
override fun onBind(intent: Intent?): IBinder? = null
@@ -31,18 +45,35 @@ class SyncService : Service() {
super.onCreate()
createNotificationChannel()
acquireWakeLock()
scheduleKeepAlive()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(NOTIFICATION_ID, createNotification())
ensureWakeLock()
return START_STICKY
}
override fun onDestroy() {
handler.removeCallbacks(keepAliveRunnable)
cancelAlarm()
releaseWakeLock()
super.onDestroy()
}
override fun onTaskRemoved(rootIntent: Intent?) {
// Restart service if app is swiped away
val restartServiceIntent = Intent(applicationContext, SyncService::class.java)
restartServiceIntent.setPackage(packageName)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(restartServiceIntent)
} else {
startService(restartServiceIntent)
}
super.onTaskRemoved(rootIntent)
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
@@ -69,7 +100,7 @@ class SyncService : Service() {
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Paarrot")
.setContentText("Syncing messages...")
.setContentText("Connected to Matrix")
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
@@ -77,16 +108,64 @@ class SyncService : Service() {
.build()
}
private fun scheduleKeepAlive() {
// Use handler for periodic wake lock refresh
handler.postDelayed(keepAliveRunnable, KEEP_ALIVE_INTERVAL_MS)
// Also set up alarm as backup
scheduleAlarm()
}
private fun scheduleAlarm() {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(this, SyncService::class.java)
val pendingIntent = PendingIntent.getService(
this,
ALARM_REQUEST_CODE,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// Schedule inexact repeating alarm to restart service periodically
alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + KEEP_ALIVE_INTERVAL_MS,
KEEP_ALIVE_INTERVAL_MS,
pendingIntent
)
}
private fun cancelAlarm() {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(this, SyncService::class.java)
val pendingIntent = PendingIntent.getService(
this,
ALARM_REQUEST_CODE,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
alarmManager.cancel(pendingIntent)
}
private fun acquireWakeLock() {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
WAKE_LOCK_TAG
).apply {
acquire(10 * 60 * 1000L) // 10 minutes timeout
// Acquire indefinitely - the service manages its own lifecycle
acquire()
}
}
private fun ensureWakeLock() {
wakeLock?.let {
if (!it.isHeld) {
it.acquire()
}
} ?: acquireWakeLock()
}
private fun releaseWakeLock() {
wakeLock?.let {
if (it.isHeld) {

File diff suppressed because one or more lines are too long