fix: Android 9 crash - Implement getForegroundInfo() for WorkManager Expedited Work (Issue #15)
This commit fixes the critical crash on Android 9 (API 28) that occurred when using WorkManager Expedited Work for background sync operations. ## Root Cause When setExpedited() is used in WorkManager, the CoroutineWorker must implement getForegroundInfo() to return a ForegroundInfo object with a Foreground Service notification. On Android 9-11, WorkManager calls this method, but the default implementation throws: IllegalStateException: Not implemented ## Solution - Implemented getForegroundInfo() in SyncWorker - Returns ForegroundInfo with sync progress notification - Android 10+: Sets FOREGROUND_SERVICE_TYPE_DATA_SYNC for proper service typing - Added required Foreground Service permissions to AndroidManifest.xml ## Technical Changes - SyncWorker.kt: Added getForegroundInfo() override - NotificationHelper.kt: Added createSyncProgressNotification() factory method - strings.xml: Added sync_in_progress UI strings (EN + DE) - AndroidManifest.xml: Added FOREGROUND_SERVICE permissions - Version updated to 1.7.1 (versionCode 18) ## Previously Fixed (in this release) - Kernel-VPN compatibility (Wireguard interface detection) - HTTP connection lifecycle optimization (SafeSardineWrapper) - Stability improvements for sync sessions ## Testing - Tested on Android 9 (API 28) - No crash on second app start - Tested on Android 15 (API 35) - No regressions - WiFi-connect sync working correctly - Expedited work notifications display properly Fixes #15 Thanks to @roughnecks for detailed bug report and testing!
This commit is contained in:
@@ -12,6 +12,11 @@
|
||||
<!-- Battery Optimization (for WorkManager background sync) -->
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
||||
<!-- v1.7.2: Foreground Service for Expedited Work (Android 9-11) -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<!-- v1.7.2: Foreground Service Type for Android 10+ -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
|
||||
<!-- BOOT Permission - CRITICAL für Auto-Sync nach Neustart! -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
|
||||
@@ -5,8 +5,11 @@ package dev.dettmer.simplenotes.sync
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Build
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.WorkerParameters
|
||||
import dev.dettmer.simplenotes.BuildConfig
|
||||
import dev.dettmer.simplenotes.utils.Constants
|
||||
@@ -26,6 +29,35 @@ class SyncWorker(
|
||||
const val ACTION_SYNC_COMPLETED = "dev.dettmer.simplenotes.SYNC_COMPLETED"
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔧 v1.7.2: Required for expedited work on Android 9-11
|
||||
*
|
||||
* WorkManager ruft diese Methode auf um die Foreground-Notification zu erstellen
|
||||
* wenn der Worker als Expedited Work gestartet wird.
|
||||
*
|
||||
* Ab Android 12+ wird diese Methode NICHT aufgerufen (neue Expedited API).
|
||||
* Auf Android 9-11 MUSS diese Methode implementiert sein!
|
||||
*
|
||||
* @see https://developer.android.com/develop/background-work/background-tasks/persistent/getting-started/define-work#foregroundinfo
|
||||
*/
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||
val notification = NotificationHelper.createSyncProgressNotification(applicationContext)
|
||||
|
||||
// Android 10+ benötigt foregroundServiceType
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
ForegroundInfo(
|
||||
NotificationHelper.SYNC_PROGRESS_NOTIFICATION_ID,
|
||||
notification,
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
)
|
||||
} else {
|
||||
ForegroundInfo(
|
||||
NotificationHelper.SYNC_PROGRESS_NOTIFICATION_ID,
|
||||
notification
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob die App im Vordergrund ist.
|
||||
* Wenn ja, brauchen wir keine Benachrichtigung - die UI zeigt die Änderungen direkt.
|
||||
|
||||
@@ -19,6 +19,7 @@ object NotificationHelper {
|
||||
private const val CHANNEL_ID = "notes_sync_channel"
|
||||
private const val NOTIFICATION_ID = 1001
|
||||
private const val SYNC_NOTIFICATION_ID = 2
|
||||
const val SYNC_PROGRESS_NOTIFICATION_ID = 1003 // v1.7.2: For expedited work foreground notification
|
||||
private const val AUTO_CANCEL_TIMEOUT_MS = 30_000L
|
||||
|
||||
/**
|
||||
@@ -54,6 +55,26 @@ object NotificationHelper {
|
||||
Logger.d(TAG, "🗑️ Cleared old sync notifications")
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔧 v1.7.2: Erstellt Notification für Sync-Progress (Expedited Work)
|
||||
*
|
||||
* Wird von SyncWorker.getForegroundInfo() aufgerufen auf Android 9-11.
|
||||
* Muss eine gültige, sichtbare Notification zurückgeben.
|
||||
*
|
||||
* @return Notification (nicht anzeigen, nur erstellen)
|
||||
*/
|
||||
fun createSyncProgressNotification(context: Context): android.app.Notification {
|
||||
return NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
||||
.setContentTitle(context.getString(R.string.sync_in_progress))
|
||||
.setContentText(context.getString(R.string.sync_in_progress_text))
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(true)
|
||||
.setProgress(0, 0, true) // Indeterminate progress
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt Erfolgs-Notification nach Sync
|
||||
*/
|
||||
|
||||
@@ -438,6 +438,8 @@
|
||||
<!-- ============================= -->
|
||||
<string name="notification_channel_name">Notizen Synchronisierung</string>
|
||||
<string name="notification_channel_desc">Benachrichtigungen über Sync-Status</string>
|
||||
<string name="sync_in_progress">Synchronisierung läuft</string>
|
||||
<string name="sync_in_progress_text">Notizen werden synchronisiert…</string>
|
||||
<string name="notification_sync_success_title">Sync erfolgreich</string>
|
||||
<string name="notification_sync_success_message">%d Notiz(en) synchronisiert</string>
|
||||
<string name="notification_sync_failed_title">Sync fehlgeschlagen</string>
|
||||
|
||||
@@ -438,6 +438,8 @@
|
||||
<!-- ============================= -->
|
||||
<string name="notification_channel_name">Notes Synchronization</string>
|
||||
<string name="notification_channel_desc">Notifications about sync status</string>
|
||||
<string name="sync_in_progress">Syncing</string>
|
||||
<string name="sync_in_progress_text">Syncing notes…</string>
|
||||
<string name="notification_sync_success_title">Sync successful</string>
|
||||
<string name="notification_sync_success_message">%d note(s) synchronized</string>
|
||||
<string name="notification_sync_failed_title">Sync failed</string>
|
||||
|
||||
Reference in New Issue
Block a user