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.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <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 --> <!-- AndroidTV support -->
<uses-feature android:name="android.software.leanback" android:required="false" /> <uses-feature android:name="android.software.leanback" android:required="false" />

View File

@@ -1,5 +1,7 @@
package wtf.ruv.paarrot package wtf.ruv.paarrot
import android.content.Intent
import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
@@ -7,5 +9,17 @@ class MainActivity : TauriActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() enableEdgeToEdge()
super.onCreate(savedInstanceState) 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 package wtf.ruv.paarrot
import android.app.AlarmManager
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
@@ -7,8 +8,11 @@ import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper
import android.os.PowerManager import android.os.PowerManager
import android.os.SystemClock
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
/** /**
@@ -21,9 +25,19 @@ class SyncService : Service() {
private const val CHANNEL_ID = "paarrot_sync_channel" private const val CHANNEL_ID = "paarrot_sync_channel"
private const val CHANNEL_NAME = "Background Sync" private const val CHANNEL_NAME = "Background Sync"
private const val WAKE_LOCK_TAG = "Paarrot:SyncWakeLock" 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 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 override fun onBind(intent: Intent?): IBinder? = null
@@ -31,18 +45,35 @@ class SyncService : Service() {
super.onCreate() super.onCreate()
createNotificationChannel() createNotificationChannel()
acquireWakeLock() acquireWakeLock()
scheduleKeepAlive()
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(NOTIFICATION_ID, createNotification()) startForeground(NOTIFICATION_ID, createNotification())
ensureWakeLock()
return START_STICKY return START_STICKY
} }
override fun onDestroy() { override fun onDestroy() {
handler.removeCallbacks(keepAliveRunnable)
cancelAlarm()
releaseWakeLock() releaseWakeLock()
super.onDestroy() 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() { private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel( val channel = NotificationChannel(
@@ -69,7 +100,7 @@ class SyncService : Service() {
return NotificationCompat.Builder(this, CHANNEL_ID) return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Paarrot") .setContentTitle("Paarrot")
.setContentText("Syncing messages...") .setContentText("Connected to Matrix")
.setSmallIcon(android.R.drawable.ic_dialog_info) .setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_LOW) .setPriority(NotificationCompat.PRIORITY_LOW)
@@ -77,16 +108,64 @@ class SyncService : Service() {
.build() .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() { private fun acquireWakeLock() {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock( wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, PowerManager.PARTIAL_WAKE_LOCK,
WAKE_LOCK_TAG WAKE_LOCK_TAG
).apply { ).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() { private fun releaseWakeLock() {
wakeLock?.let { wakeLock?.let {
if (it.isHeld) { if (it.isHeld) {

File diff suppressed because one or more lines are too long