diff --git a/app/build.gradle b/app/build.gradle index bab1da71..ecf7ff50 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { minSdkVersion 26 targetSdkVersion 36 - versionCode 53 - versionName "1.20.0" + versionCode 54 + versionName "1.21.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt b/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt index b6c53447..59daf8e5 100644 --- a/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt +++ b/app/src/main/java/io/heckel/ntfy/service/SubscriberService.kt @@ -71,6 +71,14 @@ class SubscriberService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.d(TAG, "onStartCommand executed with startId: $startId") + + // Safety check: ensure we're in foreground state. This handles edge cases where + // onCreate() may not have been called or completed before onStartCommand(). See #1520. + if (serviceNotification == null) { + Log.d(TAG, "onStartCommand: Notification not set, initializing foreground state") + initializeForegroundState() + } + if (intent != null) { Log.d(TAG, "using an intent with action ${intent.action}") when (intent.action) { @@ -90,6 +98,15 @@ class SubscriberService : Service() { Log.init(this) // Init logs in all entry points Log.d(TAG, "Subscriber service has been created") + initializeForegroundState() + } + + /** + * Initializes the foreground state by creating the notification channel and notification, + * then calling startForeground(). This is called from onCreate() and as a safety fallback + * from onStartCommand() if onCreate() didn't complete properly. + */ + private fun initializeForegroundState() { val title = getString(R.string.channel_subscriber_notification_title) val text = if (BuildConfig.FIREBASE_AVAILABLE) { getString(R.string.channel_subscriber_notification_instant_text) @@ -113,9 +130,10 @@ class SubscriberService : Service() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && e is ForegroundServiceStartNotAllowedException) { Log.w(TAG, "Cannot start foreground service from background, stopping: ${e.message}") stopSelf() - return } else { - throw e + Log.w(TAG, "Failed to start foreground: ${e.message}") + // Don't rethrow: let the service continue and hope for the best, + // or Android will kill it. Either way, we don't crash the app (see #1520). } } } @@ -134,7 +152,6 @@ class SubscriberService : Service() { } Log.d(TAG, "Starting the foreground service task") isServiceStarted = true - saveServiceState(this, ServiceState.STARTED) wakeLock = (getSystemService(POWER_SERVICE) as PowerManager).run { newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG) } @@ -164,7 +181,6 @@ class SubscriberService : Service() { } isServiceStarted = false - saveServiceState(this, ServiceState.STOPPED) } private fun refreshConnections() { @@ -238,7 +254,7 @@ class SubscriberService : Service() { } // Update foreground service notification popup - if (connections.size > 0) { + if (connections.isNotEmpty()) { val title = getString(R.string.channel_subscriber_notification_title) val text = if (BuildConfig.FIREBASE_AVAILABLE) { when (instantSubscriptions.size) { @@ -357,11 +373,6 @@ class SubscriberService : Service() { STOP } - enum class ServiceState { - STARTED, - STOPPED, - } - companion object { const val TAG = "NtfySubscriberService" const val SERVICE_START_WORKER_VERSION = BuildConfig.VERSION_CODE @@ -372,20 +383,5 @@ class SubscriberService : Service() { private const val NOTIFICATION_GROUP_ID = "io.heckel.ntfy.NOTIFICATION_GROUP_SERVICE" private const val NOTIFICATION_SERVICE_ID = 2586 private const val NOTIFICATION_RECEIVED_WAKELOCK_TIMEOUT_MILLIS = 10*60*1000L /*10 minutes*/ - private const val SHARED_PREFS_ID = "SubscriberService" - private const val SHARED_PREFS_SERVICE_STATE = "ServiceState" - - fun saveServiceState(context: Context, state: ServiceState) { - val sharedPrefs = context.getSharedPreferences(SHARED_PREFS_ID, MODE_PRIVATE) - sharedPrefs.edit { - putString(SHARED_PREFS_SERVICE_STATE, state.name) - } - } - - fun readServiceState(context: Context): ServiceState { - val sharedPrefs = context.getSharedPreferences(SHARED_PREFS_ID, MODE_PRIVATE) - val value = sharedPrefs.getString(SHARED_PREFS_SERVICE_STATE, ServiceState.STOPPED.name) - return ServiceState.valueOf(value!!) - } } } diff --git a/app/src/main/java/io/heckel/ntfy/service/SubscriberServiceManager.kt b/app/src/main/java/io/heckel/ntfy/service/SubscriberServiceManager.kt index f86110c3..c51d3039 100644 --- a/app/src/main/java/io/heckel/ntfy/service/SubscriberServiceManager.kt +++ b/app/src/main/java/io/heckel/ntfy/service/SubscriberServiceManager.kt @@ -52,7 +52,14 @@ class SubscriberServiceManager(private val context: Context) { Log.d(TAG, "ServiceStartWorker: Starting foreground service (work ID: ${id})") Intent(context, SubscriberService::class.java).also { it.action = SubscriberService.Action.START.name - ContextCompat.startForegroundService(context, it) + try { + ContextCompat.startForegroundService(context, it) + } catch (e: Exception) { + // ForegroundServiceDidNotStartInTimeException or other exceptions can occur + // due to race conditions or system constraints. We log and continue; + // the service will be retried on the next refresh() call. + Log.w(TAG, "ServiceStartWorker: Failed to start foreground service: ${e.message}") + } } } else { // No instant subscriptions, stop the service using stopService() diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index a3deb0bd..a3db2ccf 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -400,4 +400,19 @@ Добавяне на заглавка Добавяне на заглавка за сървъра Заглавките ще бъдат изпращани с всяка заявка по HTTP. Всеки сървър може да има свой набор от заявки. + Не може да бъде добавен потребител ако за сървъра е зададена потребителска заглавка Authorization + Добавяна на заглавка по избор + Променяне на заглавка по избор + Адрес на услугата + Име на заглавката (напр. CF-Access-Client-Id) + Стойност на заглавката (напр. 9f3c2e4a1b2d4e) + Добавяне на заглавка на HTTP по избор, която да бъде изпращана с всяка заявка към избрания сървър. + Можете да променяте името и стойността на заглавката или да я премахнете. + Името на заглавката съдържа неприемливи знаци + Тази заглавка е запазена и се задава от ntfy + Не може да бъде добавена заглавка Authorization ако за сървъра е зададен потребител + Заглавка с това име вече има за този сървър + Добавяне + Запазване + Премахване diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index d581d2b1..dfe04752 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -349,4 +349,70 @@ 動態色彩 使用系統動態色彩 使用 ntfy 主題色彩 + 發佈至 %1$s + 標題 + 例如:有人在門口 + 訊息 + 標籤 + 例如:warning、skull + 優先順序 + 發佈 + 無法發佈訊息:%1$s + 無法發佈訊息:%1$s(代碼 %2$d) + 訊息已發佈 + 上傳中:%1$s(%2$s / %3$s) + 上傳已取消 + 標題 + 標籤 + 優先順序 + 點擊網址 + 電子郵件 + 延遲 + Markdown + 透過網址附加 + 附加本機檔案 + 電話撥打 + 點擊網址 + 例如:https://example.com/alerts/1234 + 電子郵件 + 例如:phil@example.com + 延遲傳送 + 例如:30m、1h、today 9pm(僅支援英文) + 附件網址 + 例如:https://example.com/flowers.jpg + 附件檔名 + 例如:lilies.jpg + 電話撥打 + 例如:+1234567890 + 在此輸入訊息 + 發佈訊息 + 更多選項 + 發佈通知 + 語言 + 使用系統預設 + 系統預設 + 顯示訊息列 + 在主題檢視底部顯示訊息列 + 在主題檢視底部顯示發佈按鈕 + 自訂標頭 + 定義隨每次請求一併傳送的自訂 HTTP 標頭,例如當你的 ntfy 伺服器位於需要驗證的代理或通道之後。 + 新增標頭 + 為伺服器新增標頭 + 標頭會隨每次 HTTP 請求一併傳送。每個 ntfy 伺服器都可以有自己的一組自訂標頭。 + 若此伺服器已設定自訂 Authorization 標頭,則無法新增使用者 + 新增自訂標頭 + 編輯自訂標頭 + 服務網址 + 標頭名稱(例如:CF-Access-Client-Id) + 標頭值(例如:9f3c2e4a1b2d4e) + 新增一個會隨每次請求傳送至指定伺服器的自訂 HTTP 標頭。 + 你可以編輯所選標頭的名稱或值,或將其刪除。 + 標頭名稱包含無效字元 + 此標頭為保留項目,並由 ntfy 設定 + 若此伺服器已設定使用者,則無法新增 Authorization 標頭 + 此伺服器已存在相同名稱的標頭 + 新增 + 儲存 + 刪除 + 如需範例與所有發佈功能的詳細說明,請參考 說明文件 diff --git a/fastlane/metadata/android/en-US/changelog/NEXT.txt b/fastlane/metadata/android/en-US/changelog/54.txt similarity index 93% rename from fastlane/metadata/android/en-US/changelog/NEXT.txt rename to fastlane/metadata/android/en-US/changelog/54.txt index 51527f9a..36434ad4 100644 --- a/fastlane/metadata/android/en-US/changelog/NEXT.txt +++ b/fastlane/metadata/android/en-US/changelog/54.txt @@ -10,3 +10,4 @@ Maintenance + bug fixes: * Add support for (technically incorrect) 'image/jpg' MIME type (ntfy-android#142, thanks to @Murilobeluco) * Unify "copy to clipboard" notifications, use Android 13 style (ntfy-android#61, thanks to @thgoebel) * Fix crash in user add dialog (onAddUser) +* Fix ForegroundServiceDidNotStartInTimeException (attempt 2, see #1520)